feat-db_abstraction #2
@@ -24,6 +24,7 @@ import (
|
||||
"easywish/config"
|
||||
docs "easywish/docs"
|
||||
"easywish/internal/controllers"
|
||||
"easywish/internal/database"
|
||||
"easywish/internal/logger"
|
||||
"easywish/internal/routes"
|
||||
"easywish/internal/services"
|
||||
@@ -43,6 +44,7 @@ func main() {
|
||||
logger.NewLogger,
|
||||
gin.Default,
|
||||
),
|
||||
database.Module,
|
||||
services.Module,
|
||||
controllers.Module,
|
||||
routes.Module,
|
||||
|
||||
@@ -128,6 +128,17 @@ const docTemplate = `{
|
||||
"Auth"
|
||||
],
|
||||
"summary": "Register an account",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "desc",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/models.RegistrationBeginRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {}
|
||||
}
|
||||
},
|
||||
@@ -310,6 +321,21 @@ const docTemplate = `{
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"models.RegistrationBeginRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"email": {
|
||||
"type": "string"
|
||||
},
|
||||
"password": {
|
||||
"description": "TODO: password checking",
|
||||
"type": "string"
|
||||
},
|
||||
"username": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"securityDefinitions": {
|
||||
|
||||
@@ -124,6 +124,17 @@
|
||||
"Auth"
|
||||
],
|
||||
"summary": "Register an account",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "desc",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/models.RegistrationBeginRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {}
|
||||
}
|
||||
},
|
||||
@@ -306,6 +317,21 @@
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"models.RegistrationBeginRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"email": {
|
||||
"type": "string"
|
||||
},
|
||||
"password": {
|
||||
"description": "TODO: password checking",
|
||||
"type": "string"
|
||||
},
|
||||
"username": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"securityDefinitions": {
|
||||
|
||||
@@ -21,6 +21,16 @@ definitions:
|
||||
refresh_token:
|
||||
type: string
|
||||
type: object
|
||||
models.RegistrationBeginRequest:
|
||||
properties:
|
||||
email:
|
||||
type: string
|
||||
password:
|
||||
description: 'TODO: password checking'
|
||||
type: string
|
||||
username:
|
||||
type: string
|
||||
type: object
|
||||
info:
|
||||
contact: {}
|
||||
description: Easy and feature-rich wishlist.
|
||||
@@ -97,6 +107,13 @@ paths:
|
||||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
parameters:
|
||||
- description: desc
|
||||
in: body
|
||||
name: request
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/models.RegistrationBeginRequest'
|
||||
produces:
|
||||
- application/json
|
||||
responses: {}
|
||||
|
||||
@@ -10,6 +10,7 @@ require (
|
||||
github.com/swaggo/files v1.0.1
|
||||
github.com/swaggo/gin-swagger v1.6.0
|
||||
github.com/swaggo/swag v1.16.4
|
||||
go.uber.org/fx v1.24.0
|
||||
go.uber.org/zap v1.27.0
|
||||
)
|
||||
|
||||
@@ -32,6 +33,7 @@ require (
|
||||
github.com/goccy/go-json v0.10.5 // 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
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.10 // indirect
|
||||
@@ -50,11 +52,11 @@ require (
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||
github.com/ugorji/go/codec v1.3.0 // indirect
|
||||
go.uber.org/dig v1.19.0 // indirect
|
||||
go.uber.org/fx v1.24.0 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
golang.org/x/arch v0.18.0 // indirect
|
||||
golang.org/x/crypto v0.39.0 // indirect
|
||||
golang.org/x/net v0.41.0 // indirect
|
||||
golang.org/x/sync v0.15.0 // indirect
|
||||
golang.org/x/sys v0.33.0 // indirect
|
||||
golang.org/x/text v0.26.0 // indirect
|
||||
golang.org/x/tools v0.34.0 // indirect
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"easywish/internal/models"
|
||||
"easywish/internal/services"
|
||||
"net/http"
|
||||
|
||||
@@ -72,9 +73,25 @@ func (a *authControllerImpl) Refresh(c *gin.Context) {
|
||||
// @Tags Auth
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param request body models.RegistrationBeginRequest true "desc"
|
||||
// @Router /auth/registrationBegin [post]
|
||||
func (a *authControllerImpl) RegistrationBegin(c *gin.Context) {
|
||||
c.Status(http.StatusNotImplemented)
|
||||
|
||||
var request models.RegistrationBeginRequest
|
||||
|
||||
if err := c.ShouldBindJSON(&request); err != nil {
|
||||
c.Status(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
_, err := a.authService.RegistrationBegin(request)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.Status(http.StatusAccepted)
|
||||
return
|
||||
}
|
||||
|
||||
// RegistrationBegin implements AuthController.
|
||||
|
||||
49
backend/internal/database/dbContext.go
Normal file
49
backend/internal/database/dbContext.go
Normal file
@@ -0,0 +1,49 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"easywish/config"
|
||||
|
||||
"github.com/jackc/pgx/v5"
|
||||
"github.com/jackc/pgx/v5/pgconn"
|
||||
"github.com/jackc/pgx/v5/pgxpool"
|
||||
)
|
||||
|
||||
type DbContext interface {
|
||||
DBTX
|
||||
Close()
|
||||
BeginTx(ctx context.Context) (pgx.Tx, error)
|
||||
}
|
||||
|
||||
type dbContextImpl struct {
|
||||
Pool *pgxpool.Pool
|
||||
}
|
||||
|
||||
func NewDbContext() DbContext {
|
||||
pool, err := pgxpool.New(context.Background(), config.GetConfig().DatabaseUrl)
|
||||
if err != nil {
|
||||
panic("db connection failed: " + err.Error())
|
||||
}
|
||||
return &dbContextImpl{Pool: pool}
|
||||
}
|
||||
|
||||
func (d *dbContextImpl) Close() {
|
||||
d.Pool.Close()
|
||||
}
|
||||
|
||||
func (d *dbContextImpl) Exec(ctx context.Context, sql string, args ...any) (pgconn.CommandTag, error) {
|
||||
return d.Pool.Exec(ctx, sql, args...)
|
||||
}
|
||||
|
||||
func (d *dbContextImpl) Query(ctx context.Context, sql string, args ...any) (pgx.Rows, error) {
|
||||
return d.Pool.Query(ctx, sql, args...)
|
||||
}
|
||||
|
||||
func (d *dbContextImpl) QueryRow(ctx context.Context, sql string, args ...any) pgx.Row {
|
||||
return d.Pool.QueryRow(ctx, sql, args...)
|
||||
}
|
||||
|
||||
func (d *dbContextImpl) BeginTx(ctx context.Context) (pgx.Tx, error) {
|
||||
return d.Pool.Begin(ctx)
|
||||
}
|
||||
13
backend/internal/database/setup.go
Normal file
13
backend/internal/database/setup.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"go.uber.org/fx"
|
||||
)
|
||||
|
||||
var Module = fx.Module("database",
|
||||
fx.Provide(
|
||||
NewDbContext,
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@@ -9,4 +9,5 @@ var (
|
||||
ErrUsernameTaken = errors.New("Provided username is already in use")
|
||||
ErrInvalidCredentials = errors.New("Invalid username, password or TOTP code")
|
||||
ErrInvalidToken = errors.New("Token is invalid or expired")
|
||||
ErrServerError = errors.New("Internal server error")
|
||||
)
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"context"
|
||||
"easywish/internal/database"
|
||||
errs "easywish/internal/errors"
|
||||
"easywish/internal/logger"
|
||||
"easywish/internal/models"
|
||||
"easywish/internal/utils"
|
||||
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type AuthService interface {
|
||||
@@ -15,14 +19,29 @@ type AuthService interface {
|
||||
}
|
||||
|
||||
type authServiceImpl struct {
|
||||
log logger.Logger
|
||||
dbctx database.DbContext
|
||||
}
|
||||
|
||||
func NewAuthService() AuthService {
|
||||
return &authServiceImpl{}
|
||||
func NewAuthService(_log logger.Logger, _dbctx database.DbContext) AuthService {
|
||||
return &authServiceImpl{log: _log, dbctx: _dbctx}
|
||||
}
|
||||
|
||||
func (a *authServiceImpl) RegistrationBegin(request models.RegistrationBeginRequest) (bool, error) {
|
||||
return false, errs.ErrNotImplemented
|
||||
|
||||
ctx := context.Background()
|
||||
queries := database.New(a.dbctx)
|
||||
user, err := queries.CreateUser(ctx, request.Username) // TODO: validation
|
||||
|
||||
if err != nil {
|
||||
a.log.Get().Error("Failed to add user to database", zap.Error(err))
|
||||
return false, errs.ErrServerError
|
||||
}
|
||||
a.log.Get().Info("Registered a new user", zap.String("username", user.Username))
|
||||
|
||||
// TODO: Send verification email
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (a *authServiceImpl) RegistrationComplete(request models.RegistrationBeginRequest) (*models.RegistrationCompleteResponse, error) {
|
||||
|
||||
@@ -7,6 +7,8 @@ sql:
|
||||
go:
|
||||
out: "../backend/internal/database"
|
||||
sql_package: "pgx/v5"
|
||||
emit_prepared_queries: true
|
||||
emit_interface: false
|
||||
database:
|
||||
# managed: true
|
||||
uri: "postgresql://postgres:postgres@localhost:5432/postgres?sslmode=disable"
|
||||
|
||||
Reference in New Issue
Block a user