feat: registrationBegin method without email;

fix: missing sqlc query parameter name;
feat: util for generating security codes;
feat: enums package
This commit is contained in:
2025-06-23 16:23:46 +03:00
parent 1b55498b00
commit 0a00a5ee2b
7 changed files with 155 additions and 94 deletions

View File

@@ -12,11 +12,11 @@ type BannedUser struct {
ID int64 ID int64
UserID int64 UserID int64
Date pgtype.Timestamp Date pgtype.Timestamp
Reason pgtype.Text Reason *string
ExpiresAt pgtype.Timestamp ExpiresAt pgtype.Timestamp
BannedBy pgtype.Text BannedBy *string
Pardoned pgtype.Bool Pardoned *bool
PardonedBy pgtype.Text PardonedBy *string
} }
type ConfirmationCode struct { type ConfirmationCode struct {
@@ -25,17 +25,17 @@ type ConfirmationCode struct {
CodeType int32 CodeType int32
CodeHash string CodeHash string
ExpiresAt pgtype.Timestamp ExpiresAt pgtype.Timestamp
Used pgtype.Bool Used *bool
Deleted pgtype.Bool Deleted *bool
} }
type LoginInformation struct { type LoginInformation struct {
ID int64 ID int64
UserID int64 UserID int64
Email pgtype.Text Email *string
PasswordHash string PasswordHash string
TotpEncrypted pgtype.Text TotpEncrypted *string
Email2faEnabled pgtype.Bool Email2faEnabled *bool
PasswordChangeDate pgtype.Timestamp PasswordChangeDate pgtype.Timestamp
} }
@@ -43,41 +43,41 @@ type Profile struct {
ID int64 ID int64
UserID int64 UserID int64
Name string Name string
Bio pgtype.Text Bio *string
AvatarUrl pgtype.Text AvatarUrl *string
Birthday pgtype.Timestamp Birthday pgtype.Timestamp
Color pgtype.Text Color *string
ColorGrad pgtype.Text ColorGrad *string
} }
type ProfileSetting struct { type ProfileSetting struct {
ID int64 ID int64
ProfileID int64 ProfileID int64
HideFulfilled pgtype.Bool HideFulfilled *bool
HideProfileDetails pgtype.Bool HideProfileDetails *bool
HideForUnauthenticated pgtype.Bool HideForUnauthenticated *bool
HideBirthday pgtype.Bool HideBirthday *bool
HideDates pgtype.Bool HideDates *bool
Captcha pgtype.Bool Captcha *bool
FollowersOnlyInteraction pgtype.Bool FollowersOnlyInteraction *bool
} }
type Session struct { type Session struct {
ID int64 ID int64
UserID int64 UserID int64
Guid pgtype.UUID Guid pgtype.UUID
Name pgtype.Text Name *string
Platform pgtype.Text Platform *string
LatestIp pgtype.Text LatestIp *string
LoginTime pgtype.Timestamp LoginTime pgtype.Timestamp
LastSeenDate pgtype.Timestamp LastSeenDate pgtype.Timestamp
Terminated pgtype.Bool Terminated *bool
} }
type User struct { type User struct {
ID int64 ID int64
Username string Username string
Verified pgtype.Bool Verified *bool
RegistrationDate pgtype.Timestamp RegistrationDate pgtype.Timestamp
Deleted pgtype.Bool Deleted *bool
} }

View File

@@ -19,8 +19,8 @@ VALUES ( $1, $2, $3, $4) RETURNING id, user_id, date, reason, expires_at, banned
type CreateBannedUserParams struct { type CreateBannedUserParams struct {
UserID int64 UserID int64
ExpiresAt pgtype.Timestamp ExpiresAt pgtype.Timestamp
Reason pgtype.Text Reason *string
BannedBy pgtype.Text BannedBy *string
} }
func (q *Queries) CreateBannedUser(ctx context.Context, arg CreateBannedUserParams) (BannedUser, error) { func (q *Queries) CreateBannedUser(ctx context.Context, arg CreateBannedUserParams) (BannedUser, error) {
@@ -45,24 +45,18 @@ func (q *Queries) CreateBannedUser(ctx context.Context, arg CreateBannedUserPara
} }
const createConfirmationCode = `-- name: CreateConfirmationCode :one const createConfirmationCode = `-- name: CreateConfirmationCode :one
INSERT INTO confirmation_codes(user_id, code_type, code_hash, expires_at) INSERT INTO confirmation_codes(user_id, code_type, code_hash)
VALUES ($1, $2, crypt($3, gen_salt('bf')), $4) RETURNING id, user_id, code_type, code_hash, expires_at, used, deleted VALUES ($1, $2, crypt($3::text, gen_salt('bf'))) RETURNING id, user_id, code_type, code_hash, expires_at, used, deleted
` `
type CreateConfirmationCodeParams struct { type CreateConfirmationCodeParams struct {
UserID int64 UserID int64
CodeType int32 CodeType int32
Crypt string Code string
ExpiresAt pgtype.Timestamp
} }
func (q *Queries) CreateConfirmationCode(ctx context.Context, arg CreateConfirmationCodeParams) (ConfirmationCode, error) { func (q *Queries) CreateConfirmationCode(ctx context.Context, arg CreateConfirmationCodeParams) (ConfirmationCode, error) {
row := q.db.QueryRow(ctx, createConfirmationCode, row := q.db.QueryRow(ctx, createConfirmationCode, arg.UserID, arg.CodeType, arg.Code)
arg.UserID,
arg.CodeType,
arg.Crypt,
arg.ExpiresAt,
)
var i ConfirmationCode var i ConfirmationCode
err := row.Scan( err := row.Scan(
&i.ID, &i.ID,
@@ -83,7 +77,7 @@ VALUES ( $1, $2, crypt($3::text, gen_salt('bf')) ) RETURNING id, user_id, email,
type CreateLoginInformationParams struct { type CreateLoginInformationParams struct {
UserID int64 UserID int64
Email pgtype.Text Email *string
Password string Password string
} }
@@ -110,11 +104,11 @@ VALUES ($1, $2, $3, $4, $5, $6, $7) RETURNING id, user_id, name, bio, avatar_url
type CreateProfileParams struct { type CreateProfileParams struct {
UserID int64 UserID int64
Name string Name string
Bio pgtype.Text Bio *string
Birthday pgtype.Timestamp Birthday pgtype.Timestamp
AvatarUrl pgtype.Text AvatarUrl *string
Color pgtype.Text Color *string
ColorGrad pgtype.Text ColorGrad *string
} }
func (q *Queries) CreateProfile(ctx context.Context, arg CreateProfileParams) (Profile, error) { func (q *Queries) CreateProfile(ctx context.Context, arg CreateProfileParams) (Profile, error) {
@@ -170,9 +164,9 @@ VALUES ($1, $2, $3, $4) RETURNING id, user_id, guid, name, platform, latest_ip,
type CreateSessionParams struct { type CreateSessionParams struct {
UserID int64 UserID int64
Name pgtype.Text Name *string
Platform pgtype.Text Platform *string
LatestIp pgtype.Text LatestIp *string
} }
func (q *Queries) CreateSession(ctx context.Context, arg CreateSessionParams) (Session, error) { func (q *Queries) CreateSession(ctx context.Context, arg CreateSessionParams) (Session, error) {
@@ -331,18 +325,18 @@ WHERE users.username = $1 AND ($2 IS FALSE OR profile_settings.hide_for_unauthen
type GetProfileByUsernameRestrictedParams struct { type GetProfileByUsernameRestrictedParams struct {
Username string Username string
Column2 pgtype.Bool Column2 *bool
} }
type GetProfileByUsernameRestrictedRow struct { type GetProfileByUsernameRestrictedRow struct {
Username string Username string
Name string Name string
Birthday pgtype.Timestamp Birthday pgtype.Timestamp
Bio pgtype.Text Bio *string
AvatarUrl pgtype.Text AvatarUrl *string
Color pgtype.Text Color *string
ColorGrad pgtype.Text ColorGrad *string
HideProfileDetails pgtype.Bool HideProfileDetails *bool
} }
func (q *Queries) GetProfileByUsernameRestricted(ctx context.Context, arg GetProfileByUsernameRestrictedParams) (GetProfileByUsernameRestrictedRow, error) { func (q *Queries) GetProfileByUsernameRestricted(ctx context.Context, arg GetProfileByUsernameRestrictedParams) (GetProfileByUsernameRestrictedRow, error) {
@@ -405,17 +399,17 @@ LIMIT 20 OFFSET 20 * $1
` `
type GetProfilesRestrictedParams struct { type GetProfilesRestrictedParams struct {
Column1 pgtype.Int4 Column1 *int32
Column2 pgtype.Bool Column2 *bool
} }
type GetProfilesRestrictedRow struct { type GetProfilesRestrictedRow struct {
Username string Username string
Name string Name string
AvatarUrl pgtype.Text AvatarUrl *string
Color pgtype.Text Color *string
ColorGrad pgtype.Text ColorGrad *string
HideProfileDetails pgtype.Bool HideProfileDetails *bool
} }
func (q *Queries) GetProfilesRestricted(ctx context.Context, arg GetProfilesRestrictedParams) ([]GetProfilesRestrictedRow, error) { func (q *Queries) GetProfilesRestricted(ctx context.Context, arg GetProfilesRestrictedParams) ([]GetProfilesRestrictedRow, error) {
@@ -558,7 +552,7 @@ type GetUserByLoginCredentialsRow struct {
ID int64 ID int64
Username string Username string
PasswordHash string PasswordHash string
TotpEncrypted pgtype.Text TotpEncrypted *string
} }
func (q *Queries) GetUserByLoginCredentials(ctx context.Context, arg GetUserByLoginCredentialsParams) (GetUserByLoginCredentialsRow, error) { func (q *Queries) GetUserByLoginCredentials(ctx context.Context, arg GetUserByLoginCredentialsParams) (GetUserByLoginCredentialsRow, error) {
@@ -654,11 +648,11 @@ WHERE id = $1
type UpdateBannedUserParams struct { type UpdateBannedUserParams struct {
ID int64 ID int64
Reason pgtype.Text Reason *string
ExpiresAt pgtype.Timestamp ExpiresAt pgtype.Timestamp
BannedBy pgtype.Text BannedBy *string
Pardoned pgtype.Bool Pardoned *bool
PardonedBy pgtype.Text PardonedBy *string
} }
func (q *Queries) UpdateBannedUser(ctx context.Context, arg UpdateBannedUserParams) error { func (q *Queries) UpdateBannedUser(ctx context.Context, arg UpdateBannedUserParams) error {
@@ -681,8 +675,8 @@ WHERE id = $1
type UpdateConfirmationCodeParams struct { type UpdateConfirmationCodeParams struct {
ID int64 ID int64
Used pgtype.Bool Used *bool
Deleted pgtype.Bool Deleted *bool
} }
func (q *Queries) UpdateConfirmationCode(ctx context.Context, arg UpdateConfirmationCodeParams) error { func (q *Queries) UpdateConfirmationCode(ctx context.Context, arg UpdateConfirmationCodeParams) error {
@@ -699,10 +693,10 @@ WHERE users.username = $1 AND login_informations.user_id = users.id
type UpdateLoginInformationByUsernameParams struct { type UpdateLoginInformationByUsernameParams struct {
Username string Username string
Email pgtype.Text Email *string
Password string Password string
TotpEncrypted pgtype.Text TotpEncrypted *string
Email2faEnabled pgtype.Bool Email2faEnabled *bool
PasswordChangeDate pgtype.Timestamp PasswordChangeDate pgtype.Timestamp
} }
@@ -728,11 +722,11 @@ WHERE username = $1
type UpdateProfileByUsernameParams struct { type UpdateProfileByUsernameParams struct {
Username string Username string
Name string Name string
Bio pgtype.Text Bio *string
Birthday pgtype.Timestamp Birthday pgtype.Timestamp
AvatarUrl pgtype.Text AvatarUrl *string
Color pgtype.Text Color *string
ColorGrad pgtype.Text ColorGrad *string
} }
func (q *Queries) UpdateProfileByUsername(ctx context.Context, arg UpdateProfileByUsernameParams) error { func (q *Queries) UpdateProfileByUsername(ctx context.Context, arg UpdateProfileByUsernameParams) error {
@@ -763,13 +757,13 @@ WHERE id = $1
type UpdateProfileSettingsParams struct { type UpdateProfileSettingsParams struct {
ID int64 ID int64
HideFulfilled pgtype.Bool HideFulfilled *bool
HideProfileDetails pgtype.Bool HideProfileDetails *bool
HideForUnauthenticated pgtype.Bool HideForUnauthenticated *bool
HideBirthday pgtype.Bool HideBirthday *bool
HideDates pgtype.Bool HideDates *bool
Captcha pgtype.Bool Captcha *bool
FollowersOnlyInteraction pgtype.Bool FollowersOnlyInteraction *bool
} }
func (q *Queries) UpdateProfileSettings(ctx context.Context, arg UpdateProfileSettingsParams) error { func (q *Queries) UpdateProfileSettings(ctx context.Context, arg UpdateProfileSettingsParams) error {
@@ -794,12 +788,12 @@ WHERE id = $1
type UpdateSessionParams struct { type UpdateSessionParams struct {
ID int64 ID int64
Name pgtype.Text Name *string
Platform pgtype.Text Platform *string
LatestIp pgtype.Text LatestIp *string
LoginTime pgtype.Timestamp LoginTime pgtype.Timestamp
LastSeenDate pgtype.Timestamp LastSeenDate pgtype.Timestamp
Terminated pgtype.Bool Terminated *bool
} }
func (q *Queries) UpdateSession(ctx context.Context, arg UpdateSessionParams) error { func (q *Queries) UpdateSession(ctx context.Context, arg UpdateSessionParams) error {
@@ -823,8 +817,8 @@ WHERE id = $1
type UpdateUserParams struct { type UpdateUserParams struct {
ID int64 ID int64
Verified pgtype.Bool Verified *bool
Deleted pgtype.Bool Deleted *bool
} }
func (q *Queries) UpdateUser(ctx context.Context, arg UpdateUserParams) error { func (q *Queries) UpdateUser(ctx context.Context, arg UpdateUserParams) error {
@@ -840,8 +834,8 @@ WHERE username = $1
type UpdateUserByUsernameParams struct { type UpdateUserByUsernameParams struct {
Username string Username string
Verified pgtype.Bool Verified *bool
Deleted pgtype.Bool Deleted *bool
} }
func (q *Queries) UpdateUserByUsername(ctx context.Context, arg UpdateUserByUsernameParams) error { func (q *Queries) UpdateUserByUsername(ctx context.Context, arg UpdateUserByUsernameParams) error {

View File

@@ -5,6 +5,7 @@ import (
errs "easywish/internal/errors" errs "easywish/internal/errors"
"easywish/internal/models" "easywish/internal/models"
"easywish/internal/utils" "easywish/internal/utils"
"easywish/internal/utils/enums"
"go.uber.org/zap" "go.uber.org/zap"
) )
@@ -27,18 +28,50 @@ func NewAuthService(_log *zap.Logger, _dbctx database.DbContext) AuthService {
func (a *authServiceImpl) RegistrationBegin(request models.RegistrationBeginRequest) (bool, error) { func (a *authServiceImpl) RegistrationBegin(request models.RegistrationBeginRequest) (bool, error) {
var user database.User
var generatedCode string
helper, db, _ := database.NewDbHelperTransaction(a.dbctx) helper, db, _ := database.NewDbHelperTransaction(a.dbctx)
defer helper.Rollback()
user, err := db.TXQueries.CreateUser(db.CTX, request.Username) // TODO: validation var err error
if err != nil { if user, err = db.TXQueries.CreateUser(db.CTX, request.Username); err != nil { // TODO: validation
a.log.Error("Failed to add user to database", zap.Error(err)) a.log.Error("Failed to add user to database", zap.Error(err))
return false, errs.ErrServerError 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.CreateLoginInformation(db.CTX, database.CreateLoginInformationParams{
UserID: user.ID,
Email: request.Email,
Password: request.Password, // Hashed in database
}); err != nil {
a.log.Error("Failed to add login information for user to database", zap.Error(err))
return false, errs.ErrServerError
}
if generatedCode, err = utils.GenerateSecure6DigitNumber(); err != nil {
a.log.Error("Failed to generate a registration code", zap.Error(err))
return false, errs.ErrServerError
}
if _, err = db.TXQueries.CreateConfirmationCode(db.CTX, database.CreateConfirmationCodeParams{
UserID: user.ID,
CodeType: int32(enums.RegistrationCodeType),
Code: generatedCode, // Hashed in database
}); err != nil {
a.log.Error("Failed to add registration code to database", zap.Error(err))
return false, errs.ErrServerError
}
a.log.Info("Registered a new user", zap.String("username", user.Username)) a.log.Info("Registered a new user", zap.String("username", user.Username))
helper.Commit() helper.Commit()
a.log.Debug("Declated registration code for a new user", zap.String("username", user.Username), zap.String("code", generatedCode))
// TODO: Send verification email // TODO: Send verification email
return true, nil return true, nil

View File

@@ -0,0 +1,8 @@
package enums
type ConfirmationCodeType int32
const (
RegistrationCodeType ConfirmationCodeType = iota
PasswordResetCodeType
)

View File

@@ -0,0 +1,26 @@
package utils
import (
"crypto/rand"
"fmt"
"io"
)
func GenerateSecure6DigitNumber() (string, error) {
// Generate a random number between 0 and 999999 (inclusive)
// This ensures we get a 6-digit number, including those starting with 0
max := 1000000 // Upper bound (exclusive)
b := make([]byte, 4) // A 4-byte slice is sufficient for a 32-bit integer
if _, err := io.ReadFull(rand.Reader, b); err != nil {
return "", fmt.Errorf("failed to read random bytes: %w", err)
}
// Convert bytes to an integer
// We use a simple modulo operation to get a number within our desired range.
// While this introduces a slight bias for very large ranges, for 1,000,000
// it's negligible and simpler than more complex methods like rejection sampling.
num := int(uint32(b[0])<<24 | uint32(b[1])<<16 | uint32(b[2])<<8 | uint32(b[3])) % max
return fmt.Sprintf("%06d", num), nil
}

View File

@@ -94,8 +94,8 @@ WHERE users.username = $1;
--: Confirmation Code Object {{{ --: Confirmation Code Object {{{
;-- name: CreateConfirmationCode :one ;-- name: CreateConfirmationCode :one
INSERT INTO confirmation_codes(user_id, code_type, code_hash, expires_at) INSERT INTO confirmation_codes(user_id, code_type, code_hash)
VALUES ($1, $2, crypt($3, gen_salt('bf')), $4) RETURNING *; VALUES ($1, $2, crypt(@code::text, gen_salt('bf'))) RETURNING *;
;-- name: GetConfirmationCodeByCode :one ;-- name: GetConfirmationCodeByCode :one
SELECT * FROM confirmation_codes SELECT * FROM confirmation_codes

View File

@@ -8,7 +8,7 @@ sql:
out: "../backend/internal/database" out: "../backend/internal/database"
sql_package: "pgx/v5" sql_package: "pgx/v5"
emit_prepared_queries: true emit_prepared_queries: true
emit_interface: false emit_pointers_for_null_types: true
database: database:
# managed: true # managed: true
uri: "postgresql://postgres:postgres@localhost:5432/postgres?sslmode=disable" uri: "postgresql://postgres:postgres@localhost:5432/postgres?sslmode=disable"