+1
This commit is contained in:
parent
640255357e
commit
286a0fe826
1
go.mod
1
go.mod
@ -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 (
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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()
|
||||||
|
103
internal/processor/gin_processor.go
Normal file
103
internal/processor/gin_processor.go
Normal 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",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
22
internal/processor/gin_writer.go
Normal file
22
internal/processor/gin_writer.go
Normal 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)
|
||||||
|
}
|
@ -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",
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
48
internal/resolve_dispatcher/dispatcher.go
Normal file
48
internal/resolve_dispatcher/dispatcher.go
Normal 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
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user