refactor: updated swagger;
feat: helper function in errors for checking postgres error types; feat: sql query method for finding users by their email; feat: registration begin/complete with checking existing username/email; refactor: error handling in controller
This commit is contained in:
@@ -11,7 +11,7 @@ const docTemplate = `{
|
||||
"title": "{{.Title}}",
|
||||
"contact": {},
|
||||
"license": {
|
||||
"name": "GPL 3.0"
|
||||
"name": "GPL-3.0"
|
||||
},
|
||||
"version": "{{.Version}}"
|
||||
},
|
||||
@@ -139,7 +139,11 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {}
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "desc"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/auth/registrationComplete": {
|
||||
@@ -154,7 +158,25 @@ const docTemplate = `{
|
||||
"Auth"
|
||||
],
|
||||
"summary": "Confirm with code, finish creating the account",
|
||||
"responses": {}
|
||||
"parameters": [
|
||||
{
|
||||
"description": "desc",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/models.RegistrationCompleteRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "desc",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/models.RegistrationCompleteResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/profile": {
|
||||
@@ -305,13 +327,16 @@ const docTemplate = `{
|
||||
],
|
||||
"properties": {
|
||||
"password": {
|
||||
"type": "string"
|
||||
"type": "string",
|
||||
"maxLength": 100
|
||||
},
|
||||
"totp": {
|
||||
"type": "string"
|
||||
},
|
||||
"username": {
|
||||
"type": "string"
|
||||
"type": "string",
|
||||
"maxLength": 20,
|
||||
"minLength": 3
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -346,6 +371,46 @@ const docTemplate = `{
|
||||
"minLength": 3
|
||||
}
|
||||
}
|
||||
},
|
||||
"models.RegistrationCompleteRequest": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"name",
|
||||
"username",
|
||||
"verification_code"
|
||||
],
|
||||
"properties": {
|
||||
"avatar_url": {
|
||||
"type": "string",
|
||||
"maxLength": 255
|
||||
},
|
||||
"birthday": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"maxLength": 75
|
||||
},
|
||||
"username": {
|
||||
"type": "string",
|
||||
"maxLength": 20,
|
||||
"minLength": 3
|
||||
},
|
||||
"verification_code": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"models.RegistrationCompleteResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"access_token": {
|
||||
"type": "string"
|
||||
},
|
||||
"refresh_token": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"securityDefinitions": {
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
"title": "Easywish client API",
|
||||
"contact": {},
|
||||
"license": {
|
||||
"name": "GPL 3.0"
|
||||
"name": "GPL-3.0"
|
||||
},
|
||||
"version": "1.0"
|
||||
},
|
||||
@@ -135,7 +135,11 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {}
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "desc"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/auth/registrationComplete": {
|
||||
@@ -150,7 +154,25 @@
|
||||
"Auth"
|
||||
],
|
||||
"summary": "Confirm with code, finish creating the account",
|
||||
"responses": {}
|
||||
"parameters": [
|
||||
{
|
||||
"description": "desc",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/models.RegistrationCompleteRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "desc",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/models.RegistrationCompleteResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/profile": {
|
||||
@@ -301,13 +323,16 @@
|
||||
],
|
||||
"properties": {
|
||||
"password": {
|
||||
"type": "string"
|
||||
"type": "string",
|
||||
"maxLength": 100
|
||||
},
|
||||
"totp": {
|
||||
"type": "string"
|
||||
},
|
||||
"username": {
|
||||
"type": "string"
|
||||
"type": "string",
|
||||
"maxLength": 20,
|
||||
"minLength": 3
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -342,6 +367,46 @@
|
||||
"minLength": 3
|
||||
}
|
||||
}
|
||||
},
|
||||
"models.RegistrationCompleteRequest": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"name",
|
||||
"username",
|
||||
"verification_code"
|
||||
],
|
||||
"properties": {
|
||||
"avatar_url": {
|
||||
"type": "string",
|
||||
"maxLength": 255
|
||||
},
|
||||
"birthday": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"maxLength": 75
|
||||
},
|
||||
"username": {
|
||||
"type": "string",
|
||||
"maxLength": 20,
|
||||
"minLength": 3
|
||||
},
|
||||
"verification_code": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"models.RegistrationCompleteResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"access_token": {
|
||||
"type": "string"
|
||||
},
|
||||
"refresh_token": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"securityDefinitions": {
|
||||
|
||||
@@ -8,10 +8,13 @@ definitions:
|
||||
models.LoginRequest:
|
||||
properties:
|
||||
password:
|
||||
maxLength: 100
|
||||
type: string
|
||||
totp:
|
||||
type: string
|
||||
username:
|
||||
maxLength: 20
|
||||
minLength: 3
|
||||
type: string
|
||||
required:
|
||||
- password
|
||||
@@ -39,11 +42,39 @@ definitions:
|
||||
- password
|
||||
- username
|
||||
type: object
|
||||
models.RegistrationCompleteRequest:
|
||||
properties:
|
||||
avatar_url:
|
||||
maxLength: 255
|
||||
type: string
|
||||
birthday:
|
||||
type: string
|
||||
name:
|
||||
maxLength: 75
|
||||
type: string
|
||||
username:
|
||||
maxLength: 20
|
||||
minLength: 3
|
||||
type: string
|
||||
verification_code:
|
||||
type: string
|
||||
required:
|
||||
- name
|
||||
- username
|
||||
- verification_code
|
||||
type: object
|
||||
models.RegistrationCompleteResponse:
|
||||
properties:
|
||||
access_token:
|
||||
type: string
|
||||
refresh_token:
|
||||
type: string
|
||||
type: object
|
||||
info:
|
||||
contact: {}
|
||||
description: Easy and feature-rich wishlist.
|
||||
license:
|
||||
name: GPL 3.0
|
||||
name: GPL-3.0
|
||||
title: Easywish client API
|
||||
version: "1.0"
|
||||
paths:
|
||||
@@ -124,7 +155,9 @@ paths:
|
||||
$ref: '#/definitions/models.RegistrationBeginRequest'
|
||||
produces:
|
||||
- application/json
|
||||
responses: {}
|
||||
responses:
|
||||
"200":
|
||||
description: desc
|
||||
summary: Register an account
|
||||
tags:
|
||||
- Auth
|
||||
@@ -132,9 +165,20 @@ paths:
|
||||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
parameters:
|
||||
- description: desc
|
||||
in: body
|
||||
name: request
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/models.RegistrationCompleteRequest'
|
||||
produces:
|
||||
- application/json
|
||||
responses: {}
|
||||
responses:
|
||||
"200":
|
||||
description: desc
|
||||
schema:
|
||||
$ref: '#/definitions/models.RegistrationCompleteResponse'
|
||||
summary: Confirm with code, finish creating the account
|
||||
tags:
|
||||
- Auth
|
||||
|
||||
@@ -33,6 +33,7 @@ require (
|
||||
github.com/go-playground/validator/v10 v10.26.0 // indirect
|
||||
github.com/go-viper/mapstructure/v2 v2.2.1 // indirect
|
||||
github.com/goccy/go-json v0.10.5 // indirect
|
||||
github.com/jackc/pgerrcode v0.0.0-20240316143900-6e2875d9b438 // indirect
|
||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
|
||||
github.com/jackc/puddle/v2 v2.2.2 // indirect
|
||||
|
||||
@@ -48,6 +48,8 @@ github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVI
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/jackc/pgerrcode v0.0.0-20240316143900-6e2875d9b438 h1:Dj0L5fhJ9F82ZJyVOmBx6msDp/kfd1t9GRfny/mfJA0=
|
||||
github.com/jackc/pgerrcode v0.0.0-20240316143900-6e2875d9b438/go.mod h1:a/s9Lp5W7n/DD0VrVoyJ00FbP2ytTPDVOivvn2bMlds=
|
||||
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
|
||||
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=
|
||||
|
||||
@@ -18,11 +18,13 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
errs "easywish/internal/errors"
|
||||
"easywish/internal/middleware"
|
||||
"easywish/internal/models"
|
||||
"easywish/internal/services"
|
||||
"easywish/internal/utils"
|
||||
"easywish/internal/utils/enums"
|
||||
"errors"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
@@ -96,6 +98,8 @@ func (a *authControllerImpl) Refresh(c *gin.Context) {
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param request body models.RegistrationBeginRequest true "desc"
|
||||
// @Success 200 "Account is created and awaiting verification"
|
||||
// @Success 409 "Username or email is already taken"
|
||||
// @Router /auth/registrationBegin [post]
|
||||
func (a *authControllerImpl) RegistrationBegin(c *gin.Context) {
|
||||
|
||||
@@ -108,11 +112,15 @@ func (a *authControllerImpl) RegistrationBegin(c *gin.Context) {
|
||||
_, err := a.authService.RegistrationBegin(request.Body)
|
||||
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, err.Error())
|
||||
if errors.Is(err, errs.ErrUsernameTaken) || errors.Is(err, errs.ErrEmailTaken) {
|
||||
c.Status(http.StatusConflict)
|
||||
} else {
|
||||
c.Status(http.StatusInternalServerError)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
c.Status(http.StatusAccepted)
|
||||
c.Status(http.StatusOK)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -121,16 +129,37 @@ func (a *authControllerImpl) RegistrationBegin(c *gin.Context) {
|
||||
// @Tags Auth
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param request body models.RegistrationCompleteRequest true "desc"
|
||||
// @Success 200 {object} models.RegistrationCompleteResponse "desc"
|
||||
// @Router /auth/registrationComplete [post]
|
||||
func (a *authControllerImpl) RegistrationComplete(c *gin.Context) {
|
||||
c.Status(http.StatusNotImplemented)
|
||||
request, ok := utils.GetRequest[models.RegistrationCompleteRequest](c)
|
||||
if !ok {
|
||||
c.Status(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
response, err := a.authService.RegistrationComplete(request.Body)
|
||||
|
||||
if err != nil {
|
||||
if errors.Is(err, errs.ErrForbidden) {
|
||||
c.Status(http.StatusForbidden)
|
||||
} else if errors.Is(err, errs.ErrUnauthorized) {
|
||||
c.Status(http.StatusUnauthorized)
|
||||
} else {
|
||||
c.Status(http.StatusInternalServerError)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, response)
|
||||
}
|
||||
|
||||
func (a *authControllerImpl) RegisterRoutes(group *gin.RouterGroup) {
|
||||
group.POST("/registrationBegin", middleware.RequestMiddleware[models.RegistrationBeginRequest](enums.GuestRole), a.RegistrationBegin)
|
||||
group.POST("/registrationComplete", a.RegistrationComplete)
|
||||
group.POST("/login", a.Login)
|
||||
group.POST("/refresh", a.Refresh)
|
||||
group.POST("/passwordResetBegin", a.PasswordResetBegin)
|
||||
group.POST("/passwordResetComplete", a.PasswordResetComplete)
|
||||
group.POST("/registrationComplete", middleware.RequestMiddleware[models.RegistrationCompleteRequest](enums.GuestRole), a.RegistrationComplete)
|
||||
group.POST("/login", middleware.RequestMiddleware[models.RegistrationBeginRequest](enums.GuestRole), a.Login)
|
||||
group.POST("/refresh", middleware.RequestMiddleware[models.RegistrationBeginRequest](enums.UserRole), a.Refresh)
|
||||
group.POST("/passwordResetBegin", middleware.RequestMiddleware[models.RegistrationBeginRequest](enums.GuestRole), a.PasswordResetBegin)
|
||||
group.POST("/passwordResetComplete", middleware.RequestMiddleware[models.RegistrationBeginRequest](enums.GuestRole), a.PasswordResetComplete)
|
||||
}
|
||||
|
||||
@@ -531,6 +531,25 @@ func (q *Queries) GetUserBansByUsername(ctx context.Context, username string) ([
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const getUserByEmail = `-- name: GetUserByEmail :one
|
||||
SELECT users.id, users.username, users.verified, users.registration_date, users.deleted FROM users
|
||||
JOIN login_informations linfo ON linfo.user_id = users.id
|
||||
WHERE linfo.email = $1::text
|
||||
`
|
||||
|
||||
func (q *Queries) GetUserByEmail(ctx context.Context, email string) (User, error) {
|
||||
row := q.db.QueryRow(ctx, getUserByEmail, email)
|
||||
var i User
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.Username,
|
||||
&i.Verified,
|
||||
&i.RegistrationDate,
|
||||
&i.Deleted,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const getUserByLoginCredentials = `-- name: GetUserByLoginCredentials :one
|
||||
SELECT
|
||||
users.id,
|
||||
|
||||
@@ -24,6 +24,7 @@ import (
|
||||
var (
|
||||
ErrUnauthorized = errors.New("User is not authorized")
|
||||
ErrUsernameTaken = errors.New("Provided username is already in use")
|
||||
ErrEmailTaken = errors.New("Provided email is already in use")
|
||||
ErrUserNotFound = errors.New("User was not found")
|
||||
ErrInvalidCredentials = errors.New("Invalid username, password or TOTP code")
|
||||
ErrInvalidToken = errors.New("Token is invalid or expired")
|
||||
|
||||
34
backend/internal/errors/postgres.go
Normal file
34
backend/internal/errors/postgres.go
Normal file
@@ -0,0 +1,34 @@
|
||||
// Copyright (c) 2025 Nikolai Papin
|
||||
//
|
||||
// This file is part of Easywish
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
|
||||
// the GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package errors
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/jackc/pgx/v5/pgconn"
|
||||
)
|
||||
|
||||
func IsPgErr(err error, code string) bool {
|
||||
var pgErr *pgconn.PgError
|
||||
if errors.As(err, &pgErr) {
|
||||
if pgErr.Code == code {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
@@ -24,6 +24,7 @@ import (
|
||||
"easywish/internal/utils"
|
||||
"easywish/internal/utils/enums"
|
||||
|
||||
"github.com/jackc/pgerrcode"
|
||||
"github.com/jackc/pgx/v5"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
@@ -55,11 +56,40 @@ func (a *authServiceImpl) RegistrationBegin(request models.RegistrationBeginRequ
|
||||
var err error
|
||||
|
||||
if user, err = db.TXQueries.CreateUser(db.CTX, request.Username); err != nil {
|
||||
|
||||
if errs.IsPgErr(err, pgerrcode.UniqueViolation) {
|
||||
a.log.Warn(
|
||||
"Attempted registration for a taken username",
|
||||
zap.String("username", request.Username),
|
||||
zap.Error(err))
|
||||
return false, errs.ErrUsernameTaken
|
||||
}
|
||||
|
||||
a.log.Error("Failed to add user to database", zap.Error(err))
|
||||
return false, errs.ErrServerError
|
||||
}
|
||||
|
||||
a.log.Info("Registraion of a new user", zap.String("username", user.Username), zap.Int64("id", user.ID))
|
||||
if _, err := db.TXQueries.GetUserByEmail(db.CTX, *request.Email); err == nil {
|
||||
a.log.Warn(
|
||||
"Attempted registration for a taken email",
|
||||
zap.String("email", *request.Email))
|
||||
return false, errs.ErrEmailTaken
|
||||
|
||||
} else if !errs.IsPgErr(err, pgerrcode.NoData) {
|
||||
a.log.Error(
|
||||
"Failed to check if email is not taken",
|
||||
zap.String("email", *request.Email),
|
||||
zap.Error(err))
|
||||
return false, errs.ErrServerError
|
||||
|
||||
} else {
|
||||
a.log.Debug("Verified that email is not taken", zap.String("email", *request.Email))
|
||||
}
|
||||
|
||||
a.log.Info(
|
||||
"Registraion of a new user",
|
||||
zap.String("username", user.Username),
|
||||
zap.Int64("id", user.ID))
|
||||
|
||||
if _, err = db.TXQueries.CreateLoginInformation(db.CTX, database.CreateLoginInformationParams{
|
||||
UserID: user.ID,
|
||||
@@ -109,13 +139,21 @@ func (a *authServiceImpl) RegistrationComplete(request models.RegistrationComple
|
||||
user, err = db.TXQueries.GetUserByUsername(db.CTX, request.Username)
|
||||
|
||||
if err != nil {
|
||||
a.log.Error(
|
||||
"Failed to find user attempting to complete registration",
|
||||
if errs.IsPgErr(err, pgerrcode.NoData) {
|
||||
a.log.Warn(
|
||||
"Could not find user attempting to complete registration with given username",
|
||||
zap.String("username", request.Username),
|
||||
zap.Error(err))
|
||||
return nil, errs.ErrUserNotFound
|
||||
}
|
||||
|
||||
a.log.Error(
|
||||
"Failed to get user",
|
||||
zap.String("username", request.Username),
|
||||
zap.Error(err))
|
||||
return nil, errs.ErrServerError
|
||||
}
|
||||
|
||||
confirmationCode, err = db.TXQueries.GetConfirmationCodeByCode(db.CTX, database.GetConfirmationCodeByCodeParams{
|
||||
UserID: user.ID,
|
||||
CodeType: int32(enums.RegistrationCodeType),
|
||||
@@ -123,13 +161,22 @@ func (a *authServiceImpl) RegistrationComplete(request models.RegistrationComple
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
if errs.IsPgErr(err, pgerrcode.NoData) {
|
||||
a.log.Warn(
|
||||
"User supplied wrong confirmation code for completing registration",
|
||||
"User supplied unexistent confirmation code for completing registration",
|
||||
zap.String("username", user.Username),
|
||||
zap.String("code", request.VerificationCode),
|
||||
zap.Error(err))
|
||||
return nil, errs.ErrForbidden
|
||||
}
|
||||
|
||||
a.log.Error(
|
||||
"Failed to acquire specified registration code",
|
||||
zap.String("username", user.Username),
|
||||
zap.Error(err))
|
||||
return nil, errs.ErrServerError
|
||||
}
|
||||
|
||||
err = db.TXQueries.UpdateConfirmationCode(db.CTX, database.UpdateConfirmationCodeParams{
|
||||
ID: confirmationCode.ID,
|
||||
Used: utils.NewPointer(true),
|
||||
@@ -140,8 +187,7 @@ func (a *authServiceImpl) RegistrationComplete(request models.RegistrationComple
|
||||
"Failed to update the user's registration code used state",
|
||||
zap.String("username", user.Username),
|
||||
zap.Int64("confirmation_code_id", confirmationCode.ID),
|
||||
zap.Error(err),
|
||||
)
|
||||
zap.Error(err))
|
||||
return nil, errs.ErrServerError
|
||||
}
|
||||
|
||||
@@ -179,6 +225,7 @@ func (a *authServiceImpl) RegistrationComplete(request models.RegistrationComple
|
||||
return nil, errs.ErrServerError
|
||||
}
|
||||
|
||||
// TODO: session info
|
||||
session, err = db.TXQueries.CreateSession(db.CTX, database.CreateSessionParams{
|
||||
UserID: user.ID,
|
||||
Name: utils.NewPointer("First device"),
|
||||
@@ -206,6 +253,10 @@ func (a *authServiceImpl) RegistrationComplete(request models.RegistrationComple
|
||||
|
||||
helper.Commit()
|
||||
|
||||
a.log.Info(
|
||||
"User verified registration",
|
||||
zap.String("username", request.Username))
|
||||
|
||||
response := models.RegistrationCompleteResponse{Tokens: models.Tokens{
|
||||
AccessToken: accessToken,
|
||||
RefreshToken: refreshToken,
|
||||
|
||||
@@ -19,6 +19,7 @@ services:
|
||||
POSTGRES_URL: ${POSTGRES_URL}
|
||||
REDIS_URL: ${REDIS_URL}
|
||||
MINIO_URL: ${MINIO_URL}
|
||||
ENVIRONMENT: ${ENVIRONMENT}
|
||||
ports:
|
||||
- "8080:8080"
|
||||
networks:
|
||||
|
||||
@@ -34,6 +34,11 @@ WHERE id = $1;
|
||||
SELECT * FROM users
|
||||
WHERE username = $1;
|
||||
|
||||
;-- name: GetUserByEmail :one
|
||||
SELECT users.* FROM users
|
||||
JOIN login_informations linfo ON linfo.user_id = users.id
|
||||
WHERE linfo.email = @email::text;
|
||||
|
||||
;-- name: GetUserByLoginCredentials :one
|
||||
SELECT
|
||||
users.id,
|
||||
|
||||
Reference in New Issue
Block a user