update cache instance and update rest proto
This commit is contained in:
parent
59c949f627
commit
f5687a0094
|
@ -2,6 +2,7 @@ package auth
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"git.nobla.cn/golang/aeus/metadata"
|
"git.nobla.cn/golang/aeus/metadata"
|
||||||
|
@ -32,16 +33,7 @@ type Option func(*options)
|
||||||
// Parser is a jwt parser
|
// Parser is a jwt parser
|
||||||
type options struct {
|
type options struct {
|
||||||
allows []string
|
allows []string
|
||||||
claims func() jwt.Claims
|
claims reflect.Type
|
||||||
}
|
|
||||||
|
|
||||||
// WithClaims with customer claim
|
|
||||||
// If you use it in Server, f needs to return a new jwt.Claims object each time to avoid concurrent write problems
|
|
||||||
// If you use it in Client, f only needs to return a single object to provide performance
|
|
||||||
func WithClaims(f func() jwt.Claims) Option {
|
|
||||||
return func(o *options) {
|
|
||||||
o.claims = f
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithAllow with allow path
|
// WithAllow with allow path
|
||||||
|
@ -54,6 +46,12 @@ func WithAllow(paths ...string) Option {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func WithClaims(claims reflect.Type) Option {
|
||||||
|
return func(o *options) {
|
||||||
|
o.claims = claims
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// isAllowed check if the path is allowed
|
// isAllowed check if the path is allowed
|
||||||
func isAllowed(uripath string, allows []string) bool {
|
func isAllowed(uripath string, allows []string) bool {
|
||||||
for _, str := range allows {
|
for _, str := range allows {
|
||||||
|
@ -70,7 +68,7 @@ func isAllowed(uripath string, allows []string) bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// JWT auth middleware
|
// JWT auth middleware
|
||||||
|
@ -90,21 +88,24 @@ func JWT(keyFunc jwt.Keyfunc, cbs ...Option) middleware.Middleware {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
authorizationValue, ok := md.Get(authorizationKey)
|
token, ok := md.Get(authorizationKey)
|
||||||
if !ok {
|
if !ok {
|
||||||
return errors.ErrAccessDenied
|
return errors.ErrAccessDenied
|
||||||
}
|
}
|
||||||
if !strings.HasPrefix(authorizationValue, bearerWord) {
|
if strings.HasPrefix(token, bearerWord) {
|
||||||
return errors.ErrAccessDenied
|
token = strings.TrimPrefix(token, bearerWord)
|
||||||
}
|
}
|
||||||
var (
|
var (
|
||||||
ti *jwt.Token
|
ti *jwt.Token
|
||||||
)
|
)
|
||||||
authorizationToken := strings.TrimSpace(strings.TrimPrefix(authorizationValue, bearerWord))
|
token = strings.TrimSpace(token)
|
||||||
if opts.claims != nil {
|
if opts.claims != nil {
|
||||||
ti, err = jwt.ParseWithClaims(authorizationToken, opts.claims(), keyFunc)
|
if claims, ok := reflect.New(opts.claims).Interface().(jwt.Claims); ok {
|
||||||
} else {
|
ti, err = jwt.ParseWithClaims(token, claims, keyFunc)
|
||||||
ti, err = jwt.Parse(authorizationToken, keyFunc)
|
}
|
||||||
|
}
|
||||||
|
if ti == nil {
|
||||||
|
ti, err = jwt.Parse(token, keyFunc)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, jwt.ErrTokenMalformed) || errors.Is(err, jwt.ErrTokenUnverifiable) {
|
if errors.Is(err, jwt.ErrTokenMalformed) || errors.Is(err, jwt.ErrTokenUnverifiable) {
|
||||||
|
|
|
@ -13,7 +13,7 @@ var (
|
||||||
|
|
||||||
type Cache interface {
|
type Cache interface {
|
||||||
// Get gets a cached value by key.
|
// Get gets a cached value by key.
|
||||||
Get(ctx context.Context, key string) (any, time.Time, error)
|
Get(ctx context.Context, key string) (any, error)
|
||||||
// Put stores a key-value pair into cache.
|
// Put stores a key-value pair into cache.
|
||||||
Put(ctx context.Context, key string, val any, d time.Duration) error
|
Put(ctx context.Context, key string, val any, d time.Duration) error
|
||||||
// Delete removes a key from cache.
|
// Delete removes a key from cache.
|
||||||
|
@ -22,8 +22,12 @@ type Cache interface {
|
||||||
String() string
|
String() string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Default() Cache {
|
||||||
|
return std
|
||||||
|
}
|
||||||
|
|
||||||
// Get gets a cached value by key.
|
// Get gets a cached value by key.
|
||||||
func Get(ctx context.Context, key string) (any, time.Time, error) {
|
func Get(ctx context.Context, key string) (any, error) {
|
||||||
return std.Get(ctx, key)
|
return std.Get(ctx, key)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,22 +15,22 @@ type memCache struct {
|
||||||
sync.RWMutex
|
sync.RWMutex
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *memCache) Get(ctx context.Context, key string) (interface{}, time.Time, error) {
|
func (c *memCache) Get(ctx context.Context, key string) (any, error) {
|
||||||
c.RWMutex.RLock()
|
c.RWMutex.RLock()
|
||||||
defer c.RWMutex.RUnlock()
|
defer c.RWMutex.RUnlock()
|
||||||
|
|
||||||
item, found := c.items[key]
|
item, found := c.items[key]
|
||||||
if !found {
|
if !found {
|
||||||
return nil, time.Time{}, errors.ErrNotFound
|
return nil, errors.ErrNotFound
|
||||||
}
|
}
|
||||||
if item.Expired() {
|
if item.Expired() {
|
||||||
return nil, time.Time{}, errors.ErrExpired
|
return nil, errors.ErrExpired
|
||||||
}
|
}
|
||||||
|
|
||||||
return item.Value, time.Unix(0, item.Expiration), nil
|
return item.Value, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *memCache) Put(ctx context.Context, key string, val interface{}, d time.Duration) error {
|
func (c *memCache) Put(ctx context.Context, key string, val any, d time.Duration) error {
|
||||||
var e int64
|
var e int64
|
||||||
if d == DefaultExpiration {
|
if d == DefaultExpiration {
|
||||||
d = c.opts.Expiration
|
d = c.opts.Expiration
|
||||||
|
|
|
@ -4,7 +4,7 @@ import "time"
|
||||||
|
|
||||||
// Item represents an item stored in the cache.
|
// Item represents an item stored in the cache.
|
||||||
type Item struct {
|
type Item struct {
|
||||||
Value interface{}
|
Value any
|
||||||
Expiration int64
|
Expiration int64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@ const (
|
||||||
Invalid = 1001 //payload invalid
|
Invalid = 1001 //payload invalid
|
||||||
Exists = 1002 //already exists
|
Exists = 1002 //already exists
|
||||||
Unavailable = 1003 //service unavailable
|
Unavailable = 1003 //service unavailable
|
||||||
|
Incompatible = 1004 //type incompatible
|
||||||
Timeout = 2001 //timeout
|
Timeout = 2001 //timeout
|
||||||
Expired = 2002 //expired
|
Expired = 2002 //expired
|
||||||
TokenExpired = 4002 //token expired
|
TokenExpired = 4002 //token expired
|
||||||
|
@ -29,4 +30,5 @@ var (
|
||||||
ErrUnavailable = New(Unavailable, "service unavailable")
|
ErrUnavailable = New(Unavailable, "service unavailable")
|
||||||
ErrNetworkUnreachable = New(NetworkUnreachable, "network unreachable")
|
ErrNetworkUnreachable = New(NetworkUnreachable, "network unreachable")
|
||||||
ErrConnectionRefused = New(ConnectionRefused, "connection refused")
|
ErrConnectionRefused = New(ConnectionRefused, "connection refused")
|
||||||
|
ErrIncompatible = New(Incompatible, "incompatible")
|
||||||
)
|
)
|
||||||
|
|
|
@ -31,6 +31,16 @@ type RestFieldOptions struct {
|
||||||
Format string `protobuf:"bytes,5,opt,name=format,proto3" json:"format,omitempty"`
|
Format string `protobuf:"bytes,5,opt,name=format,proto3" json:"format,omitempty"`
|
||||||
Props string `protobuf:"bytes,6,opt,name=props,proto3" json:"props,omitempty"`
|
Props string `protobuf:"bytes,6,opt,name=props,proto3" json:"props,omitempty"`
|
||||||
Rule string `protobuf:"bytes,7,opt,name=rule,proto3" json:"rule,omitempty"`
|
Rule string `protobuf:"bytes,7,opt,name=rule,proto3" json:"rule,omitempty"`
|
||||||
|
Live string `protobuf:"bytes,8,opt,name=live,proto3" json:"live,omitempty"`
|
||||||
|
Dropdown string `protobuf:"bytes,9,opt,name=dropdown,proto3" json:"dropdown,omitempty"`
|
||||||
|
Enum string `protobuf:"bytes,10,opt,name=enum,proto3" json:"enum,omitempty"`
|
||||||
|
Match string `protobuf:"bytes,11,opt,name=match,proto3" json:"match,omitempty"`
|
||||||
|
Invisible string `protobuf:"bytes,12,opt,name=invisible,proto3" json:"invisible,omitempty"`
|
||||||
|
Tooltip string `protobuf:"bytes,13,opt,name=tooltip,proto3" json:"tooltip,omitempty"`
|
||||||
|
Uploaduri string `protobuf:"bytes,14,opt,name=uploaduri,proto3" json:"uploaduri,omitempty"`
|
||||||
|
Description string `protobuf:"bytes,15,opt,name=description,proto3" json:"description,omitempty"`
|
||||||
|
Readonly string `protobuf:"bytes,16,opt,name=readonly,proto3" json:"readonly,omitempty"`
|
||||||
|
Endofnow string `protobuf:"bytes,17,opt,name=endofnow,proto3" json:"endofnow,omitempty"`
|
||||||
unknownFields protoimpl.UnknownFields
|
unknownFields protoimpl.UnknownFields
|
||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
}
|
}
|
||||||
|
@ -114,6 +124,76 @@ func (x *RestFieldOptions) GetRule() string {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (x *RestFieldOptions) GetLive() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.Live
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *RestFieldOptions) GetDropdown() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.Dropdown
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *RestFieldOptions) GetEnum() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.Enum
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *RestFieldOptions) GetMatch() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.Match
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *RestFieldOptions) GetInvisible() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.Invisible
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *RestFieldOptions) GetTooltip() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.Tooltip
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *RestFieldOptions) GetUploaduri() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.Uploaduri
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *RestFieldOptions) GetDescription() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.Description
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *RestFieldOptions) GetReadonly() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.Readonly
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *RestFieldOptions) GetEndofnow() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.Endofnow
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
type RestMessageOptions struct {
|
type RestMessageOptions struct {
|
||||||
state protoimpl.MessageState `protogen:"open.v1"`
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
Table string `protobuf:"bytes,1,opt,name=table,proto3" json:"table,omitempty"`
|
Table string `protobuf:"bytes,1,opt,name=table,proto3" json:"table,omitempty"`
|
||||||
|
@ -198,7 +278,7 @@ var File_rest_proto protoreflect.FileDescriptor
|
||||||
const file_rest_proto_rawDesc = "" +
|
const file_rest_proto_rawDesc = "" +
|
||||||
"\n" +
|
"\n" +
|
||||||
"\n" +
|
"\n" +
|
||||||
"rest.proto\x12\x04aeus\x1a google/protobuf/descriptor.proto\"\xbc\x01\n" +
|
"rest.proto\x12\x04aeus\x1a google/protobuf/descriptor.proto\"\xc6\x03\n" +
|
||||||
"\x10RestFieldOptions\x12\x12\n" +
|
"\x10RestFieldOptions\x12\x12\n" +
|
||||||
"\x04gorm\x18\x01 \x01(\tR\x04gorm\x12\x18\n" +
|
"\x04gorm\x18\x01 \x01(\tR\x04gorm\x12\x18\n" +
|
||||||
"\acomment\x18\x02 \x01(\tR\acomment\x12\x1c\n" +
|
"\acomment\x18\x02 \x01(\tR\acomment\x12\x1c\n" +
|
||||||
|
@ -206,7 +286,18 @@ const file_rest_proto_rawDesc = "" +
|
||||||
"\bposition\x18\x04 \x01(\tR\bposition\x12\x16\n" +
|
"\bposition\x18\x04 \x01(\tR\bposition\x12\x16\n" +
|
||||||
"\x06format\x18\x05 \x01(\tR\x06format\x12\x14\n" +
|
"\x06format\x18\x05 \x01(\tR\x06format\x12\x14\n" +
|
||||||
"\x05props\x18\x06 \x01(\tR\x05props\x12\x12\n" +
|
"\x05props\x18\x06 \x01(\tR\x05props\x12\x12\n" +
|
||||||
"\x04rule\x18\a \x01(\tR\x04rule\"*\n" +
|
"\x04rule\x18\a \x01(\tR\x04rule\x12\x12\n" +
|
||||||
|
"\x04live\x18\b \x01(\tR\x04live\x12\x1a\n" +
|
||||||
|
"\bdropdown\x18\t \x01(\tR\bdropdown\x12\x12\n" +
|
||||||
|
"\x04enum\x18\n" +
|
||||||
|
" \x01(\tR\x04enum\x12\x14\n" +
|
||||||
|
"\x05match\x18\v \x01(\tR\x05match\x12\x1c\n" +
|
||||||
|
"\tinvisible\x18\f \x01(\tR\tinvisible\x12\x18\n" +
|
||||||
|
"\atooltip\x18\r \x01(\tR\atooltip\x12\x1c\n" +
|
||||||
|
"\tuploaduri\x18\x0e \x01(\tR\tuploaduri\x12 \n" +
|
||||||
|
"\vdescription\x18\x0f \x01(\tR\vdescription\x12\x1a\n" +
|
||||||
|
"\breadonly\x18\x10 \x01(\tR\breadonly\x12\x1a\n" +
|
||||||
|
"\bendofnow\x18\x11 \x01(\tR\bendofnow\"*\n" +
|
||||||
"\x12RestMessageOptions\x12\x14\n" +
|
"\x12RestMessageOptions\x12\x14\n" +
|
||||||
"\x05table\x18\x01 \x01(\tR\x05table:M\n" +
|
"\x05table\x18\x01 \x01(\tR\x05table:M\n" +
|
||||||
"\x05field\x12\x1d.google.protobuf.FieldOptions\x18\x96\x97\x03 \x01(\v2\x16.aeus.RestFieldOptionsR\x05field:O\n" +
|
"\x05field\x12\x1d.google.protobuf.FieldOptions\x18\x96\x97\x03 \x01(\v2\x16.aeus.RestFieldOptionsR\x05field:O\n" +
|
||||||
|
|
|
@ -21,6 +21,16 @@ message RestFieldOptions {
|
||||||
string format = 5;
|
string format = 5;
|
||||||
string props = 6;
|
string props = 6;
|
||||||
string rule= 7;
|
string rule= 7;
|
||||||
|
string live = 8;
|
||||||
|
string dropdown = 9;
|
||||||
|
string enum = 10;
|
||||||
|
string match = 11;
|
||||||
|
string invisible = 12;
|
||||||
|
string tooltip = 13;
|
||||||
|
string uploaduri = 14;
|
||||||
|
string description = 15;
|
||||||
|
string readonly = 16;
|
||||||
|
string endofnow = 17;
|
||||||
}
|
}
|
||||||
|
|
||||||
extend google.protobuf.MessageOptions {
|
extend google.protobuf.MessageOptions {
|
||||||
|
|
|
@ -104,7 +104,7 @@ func Geerate(app *types.Applicetion) (err error) {
|
||||||
}
|
}
|
||||||
writer.Reset()
|
writer.Reset()
|
||||||
}
|
}
|
||||||
if err = writeFile(shortName+".go", []byte("package "+shortName)); err != nil {
|
if err = writeFile(path.Join(shortName, shortName+".go"), []byte("package "+shortName)); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
err = scanDir(protoDir, "third_party", func(filename string) error {
|
err = scanDir(protoDir, "third_party", func(filename string) error {
|
||||||
|
|
|
@ -116,10 +116,39 @@ func (s *Server) notFoundHandle(ctx *gin.Context) {
|
||||||
ctx.JSON(http.StatusNotFound, newResponse(errors.NotFound, "Not Found", nil))
|
ctx.JSON(http.StatusNotFound, newResponse(errors.NotFound, "Not Found", nil))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Server) CORSInterceptor() gin.HandlerFunc {
|
||||||
|
return func(c *gin.Context) {
|
||||||
|
if c.Request.Method == "OPTIONS" {
|
||||||
|
c.Writer.Header().Add("Vary", "Origin")
|
||||||
|
c.Writer.Header().Add("Vary", "Access-Control-Request-Method")
|
||||||
|
c.Writer.Header().Add("Vary", "Access-Control-Request-Headers")
|
||||||
|
c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
|
||||||
|
c.Writer.Header().Set("Access-Control-Allow-Credentials", "true")
|
||||||
|
c.Writer.Header().Set("Access-Control-Allow-Methods", "GET,HEAD,PUT,PATCH,POST,DELETE")
|
||||||
|
h := c.Request.Header.Get("Access-Control-Request-Headers")
|
||||||
|
if h != "" {
|
||||||
|
c.Writer.Header().Set("Access-Control-Allow-Headers", h)
|
||||||
|
}
|
||||||
|
c.AbortWithStatus(204)
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
c.Writer.Header().Add("Vary", "Origin")
|
||||||
|
c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
|
||||||
|
c.Writer.Header().Set("Access-Control-Allow-Credentials", "true")
|
||||||
|
h := c.Request.Header.Get("Access-Control-Request-Headers")
|
||||||
|
if h != "" {
|
||||||
|
c.Writer.Header().Set("Access-Control-Allow-Headers", h)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
c.Next()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Server) requestInterceptor() gin.HandlerFunc {
|
func (s *Server) requestInterceptor() gin.HandlerFunc {
|
||||||
return func(ginCtx *gin.Context) {
|
return func(ginCtx *gin.Context) {
|
||||||
ctx := ginCtx.Request.Context()
|
ctx := ginCtx.Request.Context()
|
||||||
next := func(ctx context.Context) error {
|
next := func(ctx context.Context) error {
|
||||||
|
ginCtx.Request = ginCtx.Request.WithContext(ctx)
|
||||||
ginCtx.Next()
|
ginCtx.Next()
|
||||||
if err := ginCtx.Errors.Last(); err != nil {
|
if err := ginCtx.Errors.Last(); err != nil {
|
||||||
return err.Err
|
return err.Err
|
||||||
|
@ -139,8 +168,8 @@ func (s *Server) requestInterceptor() gin.HandlerFunc {
|
||||||
}
|
}
|
||||||
md.Set(metadata.RequestProtocolKey, Protocol)
|
md.Set(metadata.RequestProtocolKey, Protocol)
|
||||||
md.Set(metadata.RequestPathKey, ginCtx.Request.URL.Path)
|
md.Set(metadata.RequestPathKey, ginCtx.Request.URL.Path)
|
||||||
|
md.Set("method", ginCtx.Request.Method)
|
||||||
ctx = metadata.NewContext(ctx, md)
|
ctx = metadata.NewContext(ctx, md)
|
||||||
ginCtx.Request = ginCtx.Request.WithContext(ctx)
|
|
||||||
if err := handler(ctx); err != nil {
|
if err := handler(ctx); err != nil {
|
||||||
if se, ok := err.(*errors.Error); ok {
|
if se, ok := err.(*errors.Error); ok {
|
||||||
ginCtx.AbortWithStatusJSON(http.StatusInternalServerError, newResponse(se.Code, se.Message, nil))
|
ginCtx.AbortWithStatusJSON(http.StatusInternalServerError, newResponse(se.Code, se.Message, nil))
|
||||||
|
@ -223,6 +252,9 @@ func New(cbs ...Option) *Server {
|
||||||
gin.SetMode(gin.ReleaseMode)
|
gin.SetMode(gin.ReleaseMode)
|
||||||
}
|
}
|
||||||
svr.engine = gin.New(svr.opts.ginOptions...)
|
svr.engine = gin.New(svr.opts.ginOptions...)
|
||||||
|
if svr.opts.enableCORS {
|
||||||
|
svr.engine.Use(svr.CORSInterceptor())
|
||||||
|
}
|
||||||
svr.engine.Use(svr.requestInterceptor())
|
svr.engine.Use(svr.requestInterceptor())
|
||||||
return svr
|
return svr
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,7 @@ type (
|
||||||
logger logger.Logger
|
logger logger.Logger
|
||||||
context context.Context
|
context context.Context
|
||||||
ginOptions []gin.OptionFunc
|
ginOptions []gin.OptionFunc
|
||||||
|
enableCORS bool
|
||||||
}
|
}
|
||||||
|
|
||||||
HandleFunc func(ctx *Context) (err error)
|
HandleFunc func(ctx *Context) (err error)
|
||||||
|
@ -46,6 +47,12 @@ func WithNetwork(network string) Option {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func WithCORS() Option {
|
||||||
|
return func(o *options) {
|
||||||
|
o.enableCORS = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func WithAddress(address string) Option {
|
func WithAddress(address string) Option {
|
||||||
return func(o *options) {
|
return func(o *options) {
|
||||||
o.address = address
|
o.address = address
|
||||||
|
|
Loading…
Reference in New Issue