forked from mirrors/homebox
refactor: cleanup-api-functions (#94)
* cleanup items endpoints * refactor group routes * refactor labels routes * remove old partial * refactor location routes * formatting * update names * cleanup func * speedup test runner with disable hasher * remove duplicate code
This commit is contained in:
parent
434f1fa411
commit
18488f5b15
15 changed files with 259 additions and 339 deletions
|
@ -2,14 +2,14 @@ version: "3"
|
||||||
|
|
||||||
env:
|
env:
|
||||||
HBOX_STORAGE_SQLITE_URL: .data/homebox.db?_fk=1
|
HBOX_STORAGE_SQLITE_URL: .data/homebox.db?_fk=1
|
||||||
|
UNSAFE_DISABLE_PASSWORD_PROJECTION: "yes_i_am_sure"
|
||||||
tasks:
|
tasks:
|
||||||
setup:
|
setup:
|
||||||
desc: Install dependencies
|
desc: Install dependencies
|
||||||
cmds:
|
cmds:
|
||||||
- go install github.com/swaggo/swag/cmd/swag@latest
|
- go install github.com/swaggo/swag/cmd/swag@latest
|
||||||
- cd backend && go mod tidy
|
- cd backend && go mod tidy
|
||||||
- cd frontend && pnpm install --shamefully-hoist
|
- cd frontend && pnpm install --shamefully-hoist
|
||||||
generate:
|
generate:
|
||||||
desc: |
|
desc: |
|
||||||
Generates collateral files from the backend project
|
Generates collateral files from the backend project
|
||||||
|
|
|
@ -1135,27 +1135,6 @@ const docTemplate = `{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"/v1/users/self/password": {
|
|
||||||
"put": {
|
|
||||||
"security": [
|
|
||||||
{
|
|
||||||
"Bearer": []
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"produces": [
|
|
||||||
"application/json"
|
|
||||||
],
|
|
||||||
"tags": [
|
|
||||||
"User"
|
|
||||||
],
|
|
||||||
"summary": "Update the current user's password // TODO:",
|
|
||||||
"responses": {
|
|
||||||
"204": {
|
|
||||||
"description": "No Content"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"definitions": {
|
"definitions": {
|
||||||
|
|
|
@ -1127,27 +1127,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"/v1/users/self/password": {
|
|
||||||
"put": {
|
|
||||||
"security": [
|
|
||||||
{
|
|
||||||
"Bearer": []
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"produces": [
|
|
||||||
"application/json"
|
|
||||||
],
|
|
||||||
"tags": [
|
|
||||||
"User"
|
|
||||||
],
|
|
||||||
"summary": "Update the current user's password // TODO:",
|
|
||||||
"responses": {
|
|
||||||
"204": {
|
|
||||||
"description": "No Content"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"definitions": {
|
"definitions": {
|
||||||
|
|
|
@ -1138,18 +1138,6 @@ paths:
|
||||||
summary: Update the current user
|
summary: Update the current user
|
||||||
tags:
|
tags:
|
||||||
- User
|
- User
|
||||||
/v1/users/self/password:
|
|
||||||
put:
|
|
||||||
produces:
|
|
||||||
- application/json
|
|
||||||
responses:
|
|
||||||
"204":
|
|
||||||
description: No Content
|
|
||||||
security:
|
|
||||||
- Bearer: []
|
|
||||||
summary: 'Update the current user''s password // TODO:'
|
|
||||||
tags:
|
|
||||||
- User
|
|
||||||
securityDefinitions:
|
securityDefinitions:
|
||||||
Bearer:
|
Bearer:
|
||||||
description: '"Type ''Bearer TOKEN'' to correctly set the API Key"'
|
description: '"Type ''Bearer TOKEN'' to correctly set the API Key"'
|
||||||
|
|
|
@ -63,22 +63,6 @@ func (a *app) mwAuthToken(next http.Handler) http.Handler {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// mwAdminOnly is a middleware that extends the mwAuthToken middleware to only allow
|
|
||||||
// requests from superusers.
|
|
||||||
// func (a *app) mwAdminOnly(next http.Handler) http.Handler {
|
|
||||||
// mw := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
// usr := services.UseUserCtx(r.Context())
|
|
||||||
|
|
||||||
// if !usr.IsSuperuser {
|
|
||||||
// server.RespondUnauthorized(w)
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
|
|
||||||
// next.ServeHTTP(w, r)
|
|
||||||
// })
|
|
||||||
// return a.mwAuthToken(mw)
|
|
||||||
// }
|
|
||||||
|
|
||||||
// mqStripTrailingSlash is a middleware that will strip trailing slashes from the request path.
|
// mqStripTrailingSlash is a middleware that will strip trailing slashes from the request path.
|
||||||
func mwStripTrailingSlash(next http.Handler) http.Handler {
|
func mwStripTrailingSlash(next http.Handler) http.Handler {
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
@ -111,7 +95,6 @@ func (a *app) mwStructLogger(next http.Handler) http.Handler {
|
||||||
|
|
||||||
log.Info().
|
log.Info().
|
||||||
Str("id", middleware.GetReqID(r.Context())).
|
Str("id", middleware.GetReqID(r.Context())).
|
||||||
Str("url", url).
|
|
||||||
Str("method", r.Method).
|
Str("method", r.Method).
|
||||||
Str("remote_addr", r.RemoteAddr).
|
Str("remote_addr", r.RemoteAddr).
|
||||||
Int("status", record.Status).
|
Int("status", record.Status).
|
||||||
|
|
|
@ -65,7 +65,6 @@ func (a *app) newRouter(repos *repo.AllRepos) *chi.Mux {
|
||||||
r.Get(v1Base("/users/self"), v1Ctrl.HandleUserSelf())
|
r.Get(v1Base("/users/self"), v1Ctrl.HandleUserSelf())
|
||||||
r.Put(v1Base("/users/self"), v1Ctrl.HandleUserSelfUpdate())
|
r.Put(v1Base("/users/self"), v1Ctrl.HandleUserSelfUpdate())
|
||||||
r.Delete(v1Base("/users/self"), v1Ctrl.HandleUserSelfDelete())
|
r.Delete(v1Base("/users/self"), v1Ctrl.HandleUserSelfDelete())
|
||||||
r.Put(v1Base("/users/self/password"), v1Ctrl.HandleUserUpdatePassword())
|
|
||||||
r.Post(v1Base("/users/logout"), v1Ctrl.HandleAuthLogout())
|
r.Post(v1Base("/users/logout"), v1Ctrl.HandleAuthLogout())
|
||||||
r.Get(v1Base("/users/refresh"), v1Ctrl.HandleAuthRefresh())
|
r.Get(v1Base("/users/refresh"), v1Ctrl.HandleAuthRefresh())
|
||||||
r.Put(v1Base("/users/self/change-password"), v1Ctrl.HandleUserSelfChangePassword())
|
r.Put(v1Base("/users/self/change-password"), v1Ctrl.HandleUserSelfChangePassword())
|
||||||
|
|
|
@ -33,6 +33,8 @@ type V1Controller struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type (
|
type (
|
||||||
|
ReadyFunc func() bool
|
||||||
|
|
||||||
Build struct {
|
Build struct {
|
||||||
Version string `json:"version"`
|
Version string `json:"version"`
|
||||||
Commit string `json:"commit"`
|
Commit string `json:"commit"`
|
||||||
|
@ -50,12 +52,9 @@ type (
|
||||||
)
|
)
|
||||||
|
|
||||||
func BaseUrlFunc(prefix string) func(s string) string {
|
func BaseUrlFunc(prefix string) func(s string) string {
|
||||||
v1Base := prefix + "/v1"
|
return func(s string) string {
|
||||||
prefixFunc := func(s string) string {
|
return prefix + "/v1" + s
|
||||||
return v1Base + s
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return prefixFunc
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewControllerV1(svc *services.AllServices, options ...func(*V1Controller)) *V1Controller {
|
func NewControllerV1(svc *services.AllServices, options ...func(*V1Controller)) *V1Controller {
|
||||||
|
@ -71,8 +70,6 @@ func NewControllerV1(svc *services.AllServices, options ...func(*V1Controller))
|
||||||
return ctrl
|
return ctrl
|
||||||
}
|
}
|
||||||
|
|
||||||
type ReadyFunc func() bool
|
|
||||||
|
|
||||||
// HandleBase godoc
|
// HandleBase godoc
|
||||||
// @Summary Retrieves the basic information about the API
|
// @Summary Retrieves the basic information about the API
|
||||||
// @Tags Base
|
// @Tags Base
|
||||||
|
|
|
@ -5,30 +5,17 @@ import (
|
||||||
|
|
||||||
"github.com/go-chi/chi/v5"
|
"github.com/go-chi/chi/v5"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/hay-kot/homebox/backend/internal/repo"
|
|
||||||
"github.com/hay-kot/homebox/backend/internal/services"
|
|
||||||
"github.com/hay-kot/homebox/backend/pkgs/server"
|
"github.com/hay-kot/homebox/backend/pkgs/server"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
/*
|
func (ctrl *V1Controller) routeID(w http.ResponseWriter, r *http.Request) (uuid.UUID, error) {
|
||||||
This is where we put partial snippets/functions for actions that are commonly
|
ID, err := uuid.Parse(chi.URLParam(r, "id"))
|
||||||
used within the controller class. This _hopefully_ helps with code duplication
|
|
||||||
and makes it a little more consistent when error handling and logging.
|
|
||||||
*/
|
|
||||||
|
|
||||||
// partialParseIdAndUser parses the ID from the requests URL and pulls the user
|
|
||||||
// from the context. If either of these fail, it will return an error. When an error
|
|
||||||
// occurs it will also write the error to the response. As such, if an error is returned
|
|
||||||
// from this function you can return immediately without writing to the response.
|
|
||||||
func (ctrl *V1Controller) partialParseIdAndUser(w http.ResponseWriter, r *http.Request) (uuid.UUID, *repo.UserOut, error) {
|
|
||||||
uid, err := uuid.Parse(chi.URLParam(r, "id"))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Err(err).Msg("failed to parse id")
|
log.Err(err).Msg("failed to parse id")
|
||||||
server.RespondError(w, http.StatusBadRequest, err)
|
server.RespondError(w, http.StatusBadRequest, err)
|
||||||
return uuid.Nil, &repo.UserOut{}, err
|
return uuid.Nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
user := services.UseUserCtx(r.Context())
|
return ID, nil
|
||||||
return uid, user, nil
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,20 +32,7 @@ type (
|
||||||
// @Router /v1/groups [Get]
|
// @Router /v1/groups [Get]
|
||||||
// @Security Bearer
|
// @Security Bearer
|
||||||
func (ctrl *V1Controller) HandleGroupGet() http.HandlerFunc {
|
func (ctrl *V1Controller) HandleGroupGet() http.HandlerFunc {
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return ctrl.handleGroupGeneral()
|
||||||
ctx := services.NewContext(r.Context())
|
|
||||||
|
|
||||||
group, err := ctrl.svc.Group.Get(ctx)
|
|
||||||
if err != nil {
|
|
||||||
log.Err(err).Msg("failed to get group")
|
|
||||||
server.RespondError(w, http.StatusInternalServerError, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
group.Currency = strings.ToUpper(group.Currency) // TODO: Hack to fix the currency enums being lower caseÍ
|
|
||||||
|
|
||||||
server.Respond(w, http.StatusOK, group)
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// HandleGroupUpdate godoc
|
// HandleGroupUpdate godoc
|
||||||
|
@ -57,24 +44,41 @@ func (ctrl *V1Controller) HandleGroupGet() http.HandlerFunc {
|
||||||
// @Router /v1/groups [Put]
|
// @Router /v1/groups [Put]
|
||||||
// @Security Bearer
|
// @Security Bearer
|
||||||
func (ctrl *V1Controller) HandleGroupUpdate() http.HandlerFunc {
|
func (ctrl *V1Controller) HandleGroupUpdate() http.HandlerFunc {
|
||||||
|
return ctrl.handleGroupGeneral()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ctrl *V1Controller) handleGroupGeneral() http.HandlerFunc {
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
data := repo.GroupUpdate{}
|
|
||||||
|
|
||||||
if err := server.Decode(r, &data); err != nil {
|
|
||||||
server.RespondError(w, http.StatusBadRequest, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx := services.NewContext(r.Context())
|
ctx := services.NewContext(r.Context())
|
||||||
|
|
||||||
group, err := ctrl.svc.Group.UpdateGroup(ctx, data)
|
switch r.Method {
|
||||||
if err != nil {
|
case http.MethodGet:
|
||||||
log.Err(err).Msg("failed to update group")
|
group, err := ctrl.svc.Group.Get(ctx)
|
||||||
server.RespondError(w, http.StatusInternalServerError, err)
|
if err != nil {
|
||||||
return
|
log.Err(err).Msg("failed to get group")
|
||||||
|
server.RespondError(w, http.StatusInternalServerError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
group.Currency = strings.ToUpper(group.Currency) // TODO: Hack to fix the currency enums being lower caseÍ
|
||||||
|
server.Respond(w, http.StatusOK, group)
|
||||||
|
|
||||||
|
case http.MethodPut:
|
||||||
|
data := repo.GroupUpdate{}
|
||||||
|
if err := server.Decode(r, &data); err != nil {
|
||||||
|
server.RespondError(w, http.StatusBadRequest, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
group, err := ctrl.svc.Group.UpdateGroup(ctx, data)
|
||||||
|
if err != nil {
|
||||||
|
log.Err(err).Msg("failed to update group")
|
||||||
|
server.RespondError(w, http.StatusInternalServerError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
group.Currency = strings.ToUpper(group.Currency) // TODO: Hack to fix the currency enums being lower case
|
||||||
|
server.Respond(w, http.StatusOK, group)
|
||||||
}
|
}
|
||||||
group.Currency = strings.ToUpper(group.Currency) // TODO: Hack to fix the currency enums being lower case
|
|
||||||
server.Respond(w, http.StatusOK, group)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -89,7 +93,6 @@ func (ctrl *V1Controller) HandleGroupUpdate() http.HandlerFunc {
|
||||||
func (ctrl *V1Controller) HandleGroupInvitationsCreate() http.HandlerFunc {
|
func (ctrl *V1Controller) HandleGroupInvitationsCreate() http.HandlerFunc {
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
data := GroupInvitationCreate{}
|
data := GroupInvitationCreate{}
|
||||||
|
|
||||||
if err := server.Decode(r, &data); err != nil {
|
if err := server.Decode(r, &data); err != nil {
|
||||||
log.Err(err).Msg("failed to decode user registration data")
|
log.Err(err).Msg("failed to decode user registration data")
|
||||||
server.RespondError(w, http.StatusBadRequest, err)
|
server.RespondError(w, http.StatusBadRequest, err)
|
||||||
|
|
|
@ -13,41 +13,6 @@ import (
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
func uuidList(params url.Values, key string) []uuid.UUID {
|
|
||||||
var ids []uuid.UUID
|
|
||||||
for _, id := range params[key] {
|
|
||||||
uid, err := uuid.Parse(id)
|
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
ids = append(ids, uid)
|
|
||||||
}
|
|
||||||
return ids
|
|
||||||
}
|
|
||||||
|
|
||||||
func intOrNegativeOne(s string) int {
|
|
||||||
i, err := strconv.Atoi(s)
|
|
||||||
if err != nil {
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
return i
|
|
||||||
}
|
|
||||||
|
|
||||||
func extractQuery(r *http.Request) repo.ItemQuery {
|
|
||||||
params := r.URL.Query()
|
|
||||||
|
|
||||||
page := intOrNegativeOne(params.Get("page"))
|
|
||||||
perPage := intOrNegativeOne(params.Get("perPage"))
|
|
||||||
|
|
||||||
return repo.ItemQuery{
|
|
||||||
Page: page,
|
|
||||||
PageSize: perPage,
|
|
||||||
Search: params.Get("q"),
|
|
||||||
LocationIDs: uuidList(params, "locations"),
|
|
||||||
LabelIDs: uuidList(params, "labels"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// HandleItemsGetAll godoc
|
// HandleItemsGetAll godoc
|
||||||
// @Summary Get All Items
|
// @Summary Get All Items
|
||||||
// @Tags Items
|
// @Tags Items
|
||||||
|
@ -61,6 +26,38 @@ func extractQuery(r *http.Request) repo.ItemQuery {
|
||||||
// @Router /v1/items [GET]
|
// @Router /v1/items [GET]
|
||||||
// @Security Bearer
|
// @Security Bearer
|
||||||
func (ctrl *V1Controller) HandleItemsGetAll() http.HandlerFunc {
|
func (ctrl *V1Controller) HandleItemsGetAll() http.HandlerFunc {
|
||||||
|
uuidList := func(params url.Values, key string) []uuid.UUID {
|
||||||
|
var ids []uuid.UUID
|
||||||
|
for _, id := range params[key] {
|
||||||
|
uid, err := uuid.Parse(id)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
ids = append(ids, uid)
|
||||||
|
}
|
||||||
|
return ids
|
||||||
|
}
|
||||||
|
|
||||||
|
intOrNegativeOne := func(s string) int {
|
||||||
|
i, err := strconv.Atoi(s)
|
||||||
|
if err != nil {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
|
||||||
|
extractQuery := func(r *http.Request) repo.ItemQuery {
|
||||||
|
params := r.URL.Query()
|
||||||
|
|
||||||
|
return repo.ItemQuery{
|
||||||
|
Page: intOrNegativeOne(params.Get("page")),
|
||||||
|
PageSize: intOrNegativeOne(params.Get("perPage")),
|
||||||
|
Search: params.Get("q"),
|
||||||
|
LocationIDs: uuidList(params, "locations"),
|
||||||
|
LabelIDs: uuidList(params, "labels"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
ctx := services.NewContext(r.Context())
|
ctx := services.NewContext(r.Context())
|
||||||
items, err := ctrl.svc.Items.Query(ctx, extractQuery(r))
|
items, err := ctrl.svc.Items.Query(ctx, extractQuery(r))
|
||||||
|
@ -102,31 +99,6 @@ func (ctrl *V1Controller) HandleItemsCreate() http.HandlerFunc {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// HandleItemDelete godocs
|
|
||||||
// @Summary deletes a item
|
|
||||||
// @Tags Items
|
|
||||||
// @Produce json
|
|
||||||
// @Param id path string true "Item ID"
|
|
||||||
// @Success 204
|
|
||||||
// @Router /v1/items/{id} [DELETE]
|
|
||||||
// @Security Bearer
|
|
||||||
func (ctrl *V1Controller) HandleItemDelete() http.HandlerFunc {
|
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
uid, user, err := ctrl.partialParseIdAndUser(w, r)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
err = ctrl.svc.Items.Delete(r.Context(), user.GroupID, uid)
|
|
||||||
if err != nil {
|
|
||||||
log.Err(err).Msg("failed to delete item")
|
|
||||||
server.RespondServerError(w)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
server.Respond(w, http.StatusNoContent, nil)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// HandleItemGet godocs
|
// HandleItemGet godocs
|
||||||
// @Summary Gets a item and fields
|
// @Summary Gets a item and fields
|
||||||
// @Tags Items
|
// @Tags Items
|
||||||
|
@ -136,20 +108,19 @@ func (ctrl *V1Controller) HandleItemDelete() http.HandlerFunc {
|
||||||
// @Router /v1/items/{id} [GET]
|
// @Router /v1/items/{id} [GET]
|
||||||
// @Security Bearer
|
// @Security Bearer
|
||||||
func (ctrl *V1Controller) HandleItemGet() http.HandlerFunc {
|
func (ctrl *V1Controller) HandleItemGet() http.HandlerFunc {
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return ctrl.handleItemsGeneral()
|
||||||
uid, user, err := ctrl.partialParseIdAndUser(w, r)
|
}
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
items, err := ctrl.svc.Items.GetOne(r.Context(), user.GroupID, uid)
|
// HandleItemDelete godocs
|
||||||
if err != nil {
|
// @Summary deletes a item
|
||||||
log.Err(err).Msg("failed to get item")
|
// @Tags Items
|
||||||
server.RespondServerError(w)
|
// @Produce json
|
||||||
return
|
// @Param id path string true "Item ID"
|
||||||
}
|
// @Success 204
|
||||||
server.Respond(w, http.StatusOK, items)
|
// @Router /v1/items/{id} [DELETE]
|
||||||
}
|
// @Security Bearer
|
||||||
|
func (ctrl *V1Controller) HandleItemDelete() http.HandlerFunc {
|
||||||
|
return ctrl.handleItemsGeneral()
|
||||||
}
|
}
|
||||||
|
|
||||||
// HandleItemUpdate godocs
|
// HandleItemUpdate godocs
|
||||||
|
@ -162,26 +133,53 @@ func (ctrl *V1Controller) HandleItemGet() http.HandlerFunc {
|
||||||
// @Router /v1/items/{id} [PUT]
|
// @Router /v1/items/{id} [PUT]
|
||||||
// @Security Bearer
|
// @Security Bearer
|
||||||
func (ctrl *V1Controller) HandleItemUpdate() http.HandlerFunc {
|
func (ctrl *V1Controller) HandleItemUpdate() http.HandlerFunc {
|
||||||
|
return ctrl.handleItemsGeneral()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ctrl *V1Controller) handleItemsGeneral() http.HandlerFunc {
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
body := repo.ItemUpdate{}
|
ctx := services.NewContext(r.Context())
|
||||||
if err := server.Decode(r, &body); err != nil {
|
ID, err := ctrl.routeID(w, r)
|
||||||
log.Err(err).Msg("failed to decode request body")
|
|
||||||
server.RespondError(w, http.StatusInternalServerError, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
uid, user, err := ctrl.partialParseIdAndUser(w, r)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
body.ID = uid
|
switch r.Method {
|
||||||
result, err := ctrl.svc.Items.Update(r.Context(), user.GroupID, body)
|
case http.MethodGet:
|
||||||
if err != nil {
|
items, err := ctrl.svc.Items.GetOne(r.Context(), ctx.GID, ID)
|
||||||
log.Err(err).Msg("failed to update item")
|
if err != nil {
|
||||||
server.RespondServerError(w)
|
log.Err(err).Msg("failed to get item")
|
||||||
|
server.RespondServerError(w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
server.Respond(w, http.StatusOK, items)
|
||||||
return
|
return
|
||||||
|
case http.MethodDelete:
|
||||||
|
err = ctrl.svc.Items.Delete(r.Context(), ctx.GID, ID)
|
||||||
|
if err != nil {
|
||||||
|
log.Err(err).Msg("failed to delete item")
|
||||||
|
server.RespondServerError(w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
server.Respond(w, http.StatusNoContent, nil)
|
||||||
|
return
|
||||||
|
case http.MethodPut:
|
||||||
|
body := repo.ItemUpdate{}
|
||||||
|
if err := server.Decode(r, &body); err != nil {
|
||||||
|
log.Err(err).Msg("failed to decode request body")
|
||||||
|
server.RespondError(w, http.StatusInternalServerError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
body.ID = ID
|
||||||
|
result, err := ctrl.svc.Items.Update(r.Context(), ctx.GID, body)
|
||||||
|
if err != nil {
|
||||||
|
log.Err(err).Msg("failed to update item")
|
||||||
|
server.RespondServerError(w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
server.Respond(w, http.StatusOK, result)
|
||||||
}
|
}
|
||||||
server.Respond(w, http.StatusOK, result)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -72,7 +72,7 @@ func (ctrl *V1Controller) HandleItemAttachmentCreate() http.HandlerFunc {
|
||||||
attachmentType = attachment.TypeAttachment.String()
|
attachmentType = attachment.TypeAttachment.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
id, _, err := ctrl.partialParseIdAndUser(w, r)
|
id, err := ctrl.routeID(w, r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -163,7 +163,7 @@ func (ctrl *V1Controller) HandleItemAttachmentUpdate() http.HandlerFunc {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ctrl *V1Controller) handleItemAttachmentsHandler(w http.ResponseWriter, r *http.Request) {
|
func (ctrl *V1Controller) handleItemAttachmentsHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
uid, user, err := ctrl.partialParseIdAndUser(w, r)
|
ID, err := ctrl.routeID(w, r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -181,7 +181,7 @@ func (ctrl *V1Controller) handleItemAttachmentsHandler(w http.ResponseWriter, r
|
||||||
|
|
||||||
// Token Handler
|
// Token Handler
|
||||||
case http.MethodGet:
|
case http.MethodGet:
|
||||||
token, err := ctrl.svc.Items.AttachmentToken(ctx, uid, attachmentId)
|
token, err := ctrl.svc.Items.AttachmentToken(ctx, ID, attachmentId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
switch err {
|
switch err {
|
||||||
case services.ErrNotFound:
|
case services.ErrNotFound:
|
||||||
|
@ -210,7 +210,7 @@ func (ctrl *V1Controller) handleItemAttachmentsHandler(w http.ResponseWriter, r
|
||||||
|
|
||||||
// Delete Attachment Handler
|
// Delete Attachment Handler
|
||||||
case http.MethodDelete:
|
case http.MethodDelete:
|
||||||
err = ctrl.svc.Items.AttachmentDelete(r.Context(), user.GroupID, uid, attachmentId)
|
err = ctrl.svc.Items.AttachmentDelete(r.Context(), ctx.GID, ID, attachmentId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Err(err).Msg("failed to delete attachment")
|
log.Err(err).Msg("failed to delete attachment")
|
||||||
server.RespondServerError(w)
|
server.RespondServerError(w)
|
||||||
|
@ -230,7 +230,7 @@ func (ctrl *V1Controller) handleItemAttachmentsHandler(w http.ResponseWriter, r
|
||||||
}
|
}
|
||||||
|
|
||||||
attachment.ID = attachmentId
|
attachment.ID = attachmentId
|
||||||
val, err := ctrl.svc.Items.AttachmentUpdate(ctx, uid, &attachment)
|
val, err := ctrl.svc.Items.AttachmentUpdate(ctx, ID, &attachment)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Err(err).Msg("failed to delete attachment")
|
log.Err(err).Msg("failed to delete attachment")
|
||||||
server.RespondServerError(w)
|
server.RespondServerError(w)
|
||||||
|
|
|
@ -56,7 +56,6 @@ func (ctrl *V1Controller) HandleLabelsCreate() http.HandlerFunc {
|
||||||
}
|
}
|
||||||
|
|
||||||
server.Respond(w, http.StatusCreated, label)
|
server.Respond(w, http.StatusCreated, label)
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -69,20 +68,7 @@ func (ctrl *V1Controller) HandleLabelsCreate() http.HandlerFunc {
|
||||||
// @Router /v1/labels/{id} [DELETE]
|
// @Router /v1/labels/{id} [DELETE]
|
||||||
// @Security Bearer
|
// @Security Bearer
|
||||||
func (ctrl *V1Controller) HandleLabelDelete() http.HandlerFunc {
|
func (ctrl *V1Controller) HandleLabelDelete() http.HandlerFunc {
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return ctrl.handleLabelsGeneral()
|
||||||
uid, user, err := ctrl.partialParseIdAndUser(w, r)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
err = ctrl.svc.Labels.Delete(r.Context(), user.GroupID, uid)
|
|
||||||
if err != nil {
|
|
||||||
log.Err(err).Msg("error deleting label")
|
|
||||||
server.RespondServerError(w)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
server.Respond(w, http.StatusNoContent, nil)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// HandleLabelGet godocs
|
// HandleLabelGet godocs
|
||||||
|
@ -94,27 +80,7 @@ func (ctrl *V1Controller) HandleLabelDelete() http.HandlerFunc {
|
||||||
// @Router /v1/labels/{id} [GET]
|
// @Router /v1/labels/{id} [GET]
|
||||||
// @Security Bearer
|
// @Security Bearer
|
||||||
func (ctrl *V1Controller) HandleLabelGet() http.HandlerFunc {
|
func (ctrl *V1Controller) HandleLabelGet() http.HandlerFunc {
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return ctrl.handleLabelsGeneral()
|
||||||
uid, user, err := ctrl.partialParseIdAndUser(w, r)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
labels, err := ctrl.svc.Labels.Get(r.Context(), user.GroupID, uid)
|
|
||||||
if err != nil {
|
|
||||||
if ent.IsNotFound(err) {
|
|
||||||
log.Err(err).
|
|
||||||
Str("id", uid.String()).
|
|
||||||
Msg("label not found")
|
|
||||||
server.RespondError(w, http.StatusNotFound, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
log.Err(err).Msg("error getting label")
|
|
||||||
server.RespondServerError(w)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
server.Respond(w, http.StatusOK, labels)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// HandleLabelUpdate godocs
|
// HandleLabelUpdate godocs
|
||||||
|
@ -126,25 +92,59 @@ func (ctrl *V1Controller) HandleLabelGet() http.HandlerFunc {
|
||||||
// @Router /v1/labels/{id} [PUT]
|
// @Router /v1/labels/{id} [PUT]
|
||||||
// @Security Bearer
|
// @Security Bearer
|
||||||
func (ctrl *V1Controller) HandleLabelUpdate() http.HandlerFunc {
|
func (ctrl *V1Controller) HandleLabelUpdate() http.HandlerFunc {
|
||||||
|
return ctrl.handleLabelsGeneral()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ctrl *V1Controller) handleLabelsGeneral() http.HandlerFunc {
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
body := repo.LabelUpdate{}
|
ctx := services.NewContext(r.Context())
|
||||||
if err := server.Decode(r, &body); err != nil {
|
ID, err := ctrl.routeID(w, r)
|
||||||
log.Err(err).Msg("error decoding label update data")
|
|
||||||
server.RespondError(w, http.StatusInternalServerError, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
uid, user, err := ctrl.partialParseIdAndUser(w, r)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
body.ID = uid
|
switch r.Method {
|
||||||
result, err := ctrl.svc.Labels.Update(r.Context(), user.GroupID, body)
|
case http.MethodGet:
|
||||||
if err != nil {
|
labels, err := ctrl.svc.Labels.Get(r.Context(), ctx.GID, ID)
|
||||||
log.Err(err).Msg("error updating label")
|
if err != nil {
|
||||||
server.RespondServerError(w)
|
if ent.IsNotFound(err) {
|
||||||
return
|
log.Err(err).
|
||||||
|
Str("id", ID.String()).
|
||||||
|
Msg("label not found")
|
||||||
|
server.RespondError(w, http.StatusNotFound, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Err(err).Msg("error getting label")
|
||||||
|
server.RespondServerError(w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
server.Respond(w, http.StatusOK, labels)
|
||||||
|
|
||||||
|
case http.MethodDelete:
|
||||||
|
err = ctrl.svc.Labels.Delete(r.Context(), ctx.GID, ID)
|
||||||
|
if err != nil {
|
||||||
|
log.Err(err).Msg("error deleting label")
|
||||||
|
server.RespondServerError(w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
server.Respond(w, http.StatusNoContent, nil)
|
||||||
|
|
||||||
|
case http.MethodPut:
|
||||||
|
body := repo.LabelUpdate{}
|
||||||
|
if err := server.Decode(r, &body); err != nil {
|
||||||
|
log.Err(err).Msg("error decoding label update data")
|
||||||
|
server.RespondError(w, http.StatusInternalServerError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
body.ID = ID
|
||||||
|
result, err := ctrl.svc.Labels.Update(r.Context(), ctx.GID, body)
|
||||||
|
if err != nil {
|
||||||
|
log.Err(err).Msg("error updating label")
|
||||||
|
server.RespondServerError(w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
server.Respond(w, http.StatusOK, result)
|
||||||
}
|
}
|
||||||
server.Respond(w, http.StatusOK, result)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -69,20 +69,7 @@ func (ctrl *V1Controller) HandleLocationCreate() http.HandlerFunc {
|
||||||
// @Router /v1/locations/{id} [DELETE]
|
// @Router /v1/locations/{id} [DELETE]
|
||||||
// @Security Bearer
|
// @Security Bearer
|
||||||
func (ctrl *V1Controller) HandleLocationDelete() http.HandlerFunc {
|
func (ctrl *V1Controller) HandleLocationDelete() http.HandlerFunc {
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return ctrl.handleLocationGeneral()
|
||||||
uid, user, err := ctrl.partialParseIdAndUser(w, r)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
err = ctrl.svc.Location.Delete(r.Context(), user.GroupID, uid)
|
|
||||||
if err != nil {
|
|
||||||
log.Err(err).Msg("failed to delete location")
|
|
||||||
server.RespondServerError(w)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
server.Respond(w, http.StatusNoContent, nil)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// HandleLocationGet godocs
|
// HandleLocationGet godocs
|
||||||
|
@ -94,32 +81,7 @@ func (ctrl *V1Controller) HandleLocationDelete() http.HandlerFunc {
|
||||||
// @Router /v1/locations/{id} [GET]
|
// @Router /v1/locations/{id} [GET]
|
||||||
// @Security Bearer
|
// @Security Bearer
|
||||||
func (ctrl *V1Controller) HandleLocationGet() http.HandlerFunc {
|
func (ctrl *V1Controller) HandleLocationGet() http.HandlerFunc {
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return ctrl.handleLocationGeneral()
|
||||||
uid, user, err := ctrl.partialParseIdAndUser(w, r)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
location, err := ctrl.svc.Location.GetOne(r.Context(), user.GroupID, uid)
|
|
||||||
if err != nil {
|
|
||||||
if ent.IsNotFound(err) {
|
|
||||||
log.Err(err).
|
|
||||||
Str("id", uid.String()).
|
|
||||||
Str("gid", user.GroupID.String()).
|
|
||||||
Msg("location not found")
|
|
||||||
server.RespondError(w, http.StatusNotFound, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Err(err).
|
|
||||||
Str("id", uid.String()).
|
|
||||||
Str("gid", user.GroupID.String()).
|
|
||||||
Msg("failed to get location")
|
|
||||||
server.RespondServerError(w)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
server.Respond(w, http.StatusOK, location)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// HandleLocationUpdate godocs
|
// HandleLocationUpdate godocs
|
||||||
|
@ -131,27 +93,61 @@ func (ctrl *V1Controller) HandleLocationGet() http.HandlerFunc {
|
||||||
// @Router /v1/locations/{id} [PUT]
|
// @Router /v1/locations/{id} [PUT]
|
||||||
// @Security Bearer
|
// @Security Bearer
|
||||||
func (ctrl *V1Controller) HandleLocationUpdate() http.HandlerFunc {
|
func (ctrl *V1Controller) HandleLocationUpdate() http.HandlerFunc {
|
||||||
|
return ctrl.handleLocationGeneral()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ctrl *V1Controller) handleLocationGeneral() http.HandlerFunc {
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
body := repo.LocationUpdate{}
|
ctx := services.NewContext(r.Context())
|
||||||
if err := server.Decode(r, &body); err != nil {
|
ID, err := ctrl.routeID(w, r)
|
||||||
log.Err(err).Msg("failed to decode location update data")
|
|
||||||
server.RespondError(w, http.StatusInternalServerError, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
uid, user, err := ctrl.partialParseIdAndUser(w, r)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
body.ID = uid
|
switch r.Method {
|
||||||
|
case http.MethodGet:
|
||||||
|
location, err := ctrl.svc.Location.GetOne(r.Context(), ctx.GID, ID)
|
||||||
|
if err != nil {
|
||||||
|
l := log.Err(err).
|
||||||
|
Str("ID", ID.String()).
|
||||||
|
Str("GID", ctx.GID.String())
|
||||||
|
|
||||||
result, err := ctrl.svc.Location.Update(r.Context(), user.GroupID, body)
|
if ent.IsNotFound(err) {
|
||||||
if err != nil {
|
l.Msg("location not found")
|
||||||
log.Err(err).Msg("failed to update location")
|
server.RespondError(w, http.StatusNotFound, err)
|
||||||
server.RespondServerError(w)
|
return
|
||||||
return
|
}
|
||||||
|
|
||||||
|
l.Msg("failed to get location")
|
||||||
|
server.RespondServerError(w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
server.Respond(w, http.StatusOK, location)
|
||||||
|
case http.MethodPut:
|
||||||
|
body := repo.LocationUpdate{}
|
||||||
|
if err := server.Decode(r, &body); err != nil {
|
||||||
|
log.Err(err).Msg("failed to decode location update data")
|
||||||
|
server.RespondError(w, http.StatusInternalServerError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
body.ID = ID
|
||||||
|
|
||||||
|
result, err := ctrl.svc.Location.Update(r.Context(), ctx.GID, body)
|
||||||
|
if err != nil {
|
||||||
|
log.Err(err).Msg("failed to update location")
|
||||||
|
server.RespondServerError(w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
server.Respond(w, http.StatusOK, result)
|
||||||
|
case http.MethodDelete:
|
||||||
|
err = ctrl.svc.Location.Delete(r.Context(), ctx.GID, ID)
|
||||||
|
if err != nil {
|
||||||
|
log.Err(err).Msg("failed to delete location")
|
||||||
|
server.RespondServerError(w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
server.Respond(w, http.StatusNoContent, nil)
|
||||||
}
|
}
|
||||||
server.Respond(w, http.StatusOK, result)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -85,7 +85,6 @@ func (ctrl *V1Controller) HandleUserSelfUpdate() http.HandlerFunc {
|
||||||
newData, err := ctrl.svc.User.UpdateSelf(r.Context(), actor.ID, updateData)
|
newData, err := ctrl.svc.User.UpdateSelf(r.Context(), actor.ID, updateData)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
||||||
server.RespondError(w, http.StatusInternalServerError, err)
|
server.RespondError(w, http.StatusInternalServerError, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -94,18 +93,6 @@ func (ctrl *V1Controller) HandleUserSelfUpdate() http.HandlerFunc {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// HandleUserUpdatePassword godoc
|
|
||||||
// @Summary Update the current user's password // TODO:
|
|
||||||
// @Tags User
|
|
||||||
// @Produce json
|
|
||||||
// @Success 204
|
|
||||||
// @Router /v1/users/self/password [PUT]
|
|
||||||
// @Security Bearer
|
|
||||||
func (ctrl *V1Controller) HandleUserUpdatePassword() http.HandlerFunc {
|
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// HandleUserSelfDelete godoc
|
// HandleUserSelfDelete godoc
|
||||||
// @Summary Deletes the user account
|
// @Summary Deletes the user account
|
||||||
// @Tags User
|
// @Tags User
|
||||||
|
|
|
@ -1,13 +1,37 @@
|
||||||
package hasher
|
package hasher
|
||||||
|
|
||||||
import "golang.org/x/crypto/bcrypt"
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"golang.org/x/crypto/bcrypt"
|
||||||
|
)
|
||||||
|
|
||||||
|
var enabled = true
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
disableHas := os.Getenv("UNSAFE_DISABLE_PASSWORD_PROJECTION") == "yes_i_am_sure"
|
||||||
|
|
||||||
|
if disableHas {
|
||||||
|
fmt.Println("WARNING: Password projection is disabled. This is unsafe in production.")
|
||||||
|
enabled = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func HashPassword(password string) (string, error) {
|
func HashPassword(password string) (string, error) {
|
||||||
|
if !enabled {
|
||||||
|
return password, nil
|
||||||
|
}
|
||||||
|
|
||||||
bytes, err := bcrypt.GenerateFromPassword([]byte(password), 14)
|
bytes, err := bcrypt.GenerateFromPassword([]byte(password), 14)
|
||||||
return string(bytes), err
|
return string(bytes), err
|
||||||
}
|
}
|
||||||
|
|
||||||
func CheckPasswordHash(password, hash string) bool {
|
func CheckPasswordHash(password, hash string) bool {
|
||||||
|
if !enabled {
|
||||||
|
return password == hash
|
||||||
|
}
|
||||||
|
|
||||||
err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password))
|
err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password))
|
||||||
return err == nil
|
return err == nil
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue