This commit is contained in:
r8zavetr8v 2024-12-29 15:15:44 -08:00
parent 640255357e
commit 286a0fe826
7 changed files with 213 additions and 50 deletions

1
go.mod
View File

@ -14,6 +14,7 @@ require (
github.com/spf13/viper v1.19.0 github.com/spf13/viper v1.19.0
github.com/stretchr/testify v1.9.0 github.com/stretchr/testify v1.9.0
golang.org/x/crypto v0.28.0 golang.org/x/crypto v0.28.0
golang.org/x/sync v0.8.0
) )
require ( require (

View File

@ -7,6 +7,8 @@ import (
"git.optclblast.xyz/draincloud/draincloud-core/internal/domain" "git.optclblast.xyz/draincloud/draincloud-core/internal/domain"
filesengine "git.optclblast.xyz/draincloud/draincloud-core/internal/files_engine" filesengine "git.optclblast.xyz/draincloud/draincloud-core/internal/files_engine"
"git.optclblast.xyz/draincloud/draincloud-core/internal/handler"
"git.optclblast.xyz/draincloud/draincloud-core/internal/processor"
"git.optclblast.xyz/draincloud/draincloud-core/internal/storage" "git.optclblast.xyz/draincloud/draincloud-core/internal/storage"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
) )
@ -15,6 +17,8 @@ type DrainCloud struct {
mux *gin.Engine mux *gin.Engine
database storage.Database database storage.Database
filesEngine *filesengine.FilesEngine filesEngine *filesengine.FilesEngine
ginProcessor processor.Processor[gin.HandlerFunc]
} }
func New( func New(
@ -31,7 +35,8 @@ func New(
// Built-in auth component of DrainCloud-Core // Built-in auth component of DrainCloud-Core
authGroup := mux.Group("/auth") authGroup := mux.Group("/auth")
{ {
authGroup.POST("/register", d.Register) // authGroup.POST("/register", d.Register)
authGroup.POST("/register", d.ginProcessor.Process(&handler.Handler{}))
authGroup.POST("/logon", d.Login) authGroup.POST("/logon", d.Login)
} }

View File

@ -13,6 +13,34 @@ type RequestPool struct {
sp sync.Pool sp sync.Pool
} }
func (p *RequestPool) Get() *Request {
r, _ := p.sp.Get().(*Request)
return r
}
func (p *RequestPool) Put(r *Request) {
r.ID = ""
r.Metadata = make(map[string]any)
r.ResolveValues = sync.Map{}
r.Session = nil
r.User = nil
r.Body = nil
p.sp.Put(r)
}
func NewRequestPool() *RequestPool {
return &RequestPool{
sp: sync.Pool{
New: func() any {
return &Request{
ResolveValues: sync.Map{},
Metadata: make(map[string]any),
}
},
},
}
}
type Request struct { type Request struct {
ID string ID string
Session *models.Session Session *models.Session
@ -22,8 +50,8 @@ type Request struct {
Body []byte Body []byte
} }
// NewRequest builds a new *Request struct from raw http Request. No auth data validated. // NewRequestFromHttp builds a new *Request struct from raw http Request. No auth data validated.
func NewRequest(pool *RequestPool, req *http.Request) *Request { func NewRequestFromHttp(pool *RequestPool, req *http.Request) *Request {
out := pool.sp.Get().(*Request) out := pool.sp.Get().(*Request)
cookies := req.Cookies() cookies := req.Cookies()

View File

@ -0,0 +1,103 @@
package processor
import (
"context"
"errors"
"fmt"
"net/http"
"git.optclblast.xyz/draincloud/draincloud-core/internal/common"
"git.optclblast.xyz/draincloud/draincloud-core/internal/domain"
"git.optclblast.xyz/draincloud/draincloud-core/internal/errs"
"git.optclblast.xyz/draincloud/draincloud-core/internal/handler"
"git.optclblast.xyz/draincloud/draincloud-core/internal/logger"
resolvedispatcher "git.optclblast.xyz/draincloud/draincloud-core/internal/resolve_dispatcher"
"git.optclblast.xyz/draincloud/draincloud-core/internal/storage"
"github.com/gin-gonic/gin"
"golang.org/x/sync/errgroup"
)
type GinProcessor struct {
rp *common.RequestPool
authStorage storage.AuthStorage
resolveDispatcher *resolvedispatcher.ResolveDispatcher
}
func NewGinProcessor(
authStorage storage.AuthStorage,
resolveDispatcher *resolvedispatcher.ResolveDispatcher,
) *GinProcessor {
return &GinProcessor{
rp: common.NewRequestPool(),
authStorage: authStorage,
resolveDispatcher: resolveDispatcher,
}
}
func (p *GinProcessor) Process(handler *handler.Handler) gin.HandlerFunc {
return func(ctx *gin.Context) {
req := p.rp.Get()
ctx.Request = ctx.Request.WithContext(context.WithValue(ctx.Request.Context(), "__request_id", req.ID))
// 1. Resolve the resolvers, collect all data required
// 2. Try process oprional resolvers
err := p.resolve(ctx, handler, req)
if err != nil {
p.writeError(ctx, err)
return
}
// 3. Call preprocessinf fn's, middlewares etc.
// ....
// 4. Call handler.ProcessFn
if err = handler.ProcessFn(ctx, req, wrapGin(ctx)); err != nil {
p.writeError(ctx, err)
return
}
}
}
func (p *GinProcessor) resolve(ctx context.Context, h *handler.Handler, req *common.Request) error {
eg, ctx := errgroup.WithContext(ctx)
for _, r := range h.RequiredResolveParams {
resolver, err := p.resolveDispatcher.GetResolver(r)
if err != nil {
return fmt.Errorf("failed to resolve '%s' param: no resolver provided: %w", r, err)
}
resolveValueName := r
eg.Go(func() error {
if resolveErr := resolver.Resolve(ctx, req); resolveErr != nil {
return fmt.Errorf("failed to resolve '%s' value: %w", resolveValueName, resolveErr)
}
return nil
})
}
if err := eg.Wait(); err != nil {
return err
}
return nil
}
func (p *GinProcessor) writeError(ctx *gin.Context, err error) {
logger.Error(ctx, "error process request", logger.Err(err))
switch {
case errors.Is(err, errs.ErrorAccessDenied):
ctx.JSON(http.StatusInternalServerError, domain.ErrorJson{
Code: http.StatusForbidden,
Message: err.Error(),
})
case errors.Is(err, errs.ErrorSessionExpired):
ctx.JSON(http.StatusInternalServerError, domain.ErrorJson{
Code: http.StatusForbidden,
Message: err.Error(),
})
default:
ctx.JSON(http.StatusInternalServerError, domain.ErrorJson{
Code: http.StatusInternalServerError,
Message: "Internal Error",
})
}
}

View File

@ -0,0 +1,22 @@
package processor
import (
"context"
"net/http"
"github.com/gin-gonic/gin"
)
type ginWriter struct {
ctx *gin.Context
}
func wrapGin(ctx *gin.Context) ginWriter {
return ginWriter{
ctx: ctx,
}
}
func (w ginWriter) Write(ctx context.Context, resp any) {
w.ctx.JSON(http.StatusOK, resp)
}

View File

@ -1,51 +1,7 @@
package processor package processor
import ( import "git.optclblast.xyz/draincloud/draincloud-core/internal/handler"
"errors"
"net/http"
"git.optclblast.xyz/draincloud/draincloud-core/internal/common" type Processor[H any] interface {
"git.optclblast.xyz/draincloud/draincloud-core/internal/domain" Process(*handler.Handler) H
"git.optclblast.xyz/draincloud/draincloud-core/internal/errs"
"git.optclblast.xyz/draincloud/draincloud-core/internal/handler"
"git.optclblast.xyz/draincloud/draincloud-core/internal/storage"
"github.com/gin-gonic/gin"
)
type Processor struct {
rp *common.RequestPool
authStorage storage.AuthStorage
}
func (p *Processor) Process(handler *handler.Handler) gin.HandlerFunc {
return func(ctx *gin.Context) {
//req := common.NewRequest(p.rp, ctx.Request)
// if handler.WithAuth {
// if err := p.authorize(ctx, req); err != nil {
// p.writeError(ctx, err)
// return
// }
// }
}
}
func (p *Processor) writeError(ctx *gin.Context, err error) {
switch {
case errors.Is(err, errs.ErrorAccessDenied):
ctx.JSON(http.StatusInternalServerError, domain.ErrorJson{
Code: http.StatusForbidden,
Message: err.Error(),
})
case errors.Is(err, errs.ErrorSessionExpired):
ctx.JSON(http.StatusInternalServerError, domain.ErrorJson{
Code: http.StatusForbidden,
Message: err.Error(),
})
default:
ctx.JSON(http.StatusInternalServerError, domain.ErrorJson{
Code: http.StatusInternalServerError,
Message: "Internal Error",
})
}
} }

View File

@ -0,0 +1,48 @@
package resolvedispatcher
import (
"context"
"fmt"
"sync"
"git.optclblast.xyz/draincloud/draincloud-core/internal/logger"
"git.optclblast.xyz/draincloud/draincloud-core/internal/resolvers"
)
type ResolveDispatcher struct {
m sync.RWMutex
router map[string]resolvers.Resolver
}
func New() *ResolveDispatcher {
return &ResolveDispatcher{
router: map[string]resolvers.Resolver{},
}
}
func (d *ResolveDispatcher) RegisterResolver(
ctx context.Context,
resolverName string,
resolver resolvers.Resolver,
) {
d.m.Lock()
defer d.m.Unlock()
if _, ok := d.router[resolverName]; ok {
logger.Fatal(ctx, fmt.Sprintf("resolver '%s' is already registered in router", resolverName))
}
d.router[resolverName] = resolver
}
func (d *ResolveDispatcher) GetResolver(name string) (resolvers.Resolver, error) {
d.m.RLock()
defer d.m.RUnlock()
res, ok := d.router[name]
if !ok {
return nil, fmt.Errorf("resolver '%s' not found", name)
}
return res, nil
}