示例#1
0
文件: oauth2.go 项目: micro/auth-srv
func authClient(clientId, clientSecret string) error {
	acc, err := db.Search(clientId, "", 1, 0)
	if err != nil {
		return errors.InternalServerError("go.micro.srv.auth", "server_error")
	}

	if len(acc) == 0 {
		return errors.BadRequest("go.micro.srv.auth", "invalid_request")
	}

	// check the secret
	salt, secret, err := db.SaltAndSecret(acc[0].Id)
	if err != nil {
		return errors.InternalServerError("go.micro.srv.auth", "server_error")
	}

	s, err := base64.StdEncoding.DecodeString(secret)
	if err != nil {
		return errors.InternalServerError("go.micro.srv.auth", "server_error")
	}

	// does it match?
	if err := bcrypt.CompareHashAndPassword(s, []byte(x+salt+clientSecret)); err != nil {
		return errors.BadRequest("go.micro.srv.auth", "access_denied")
	}

	return nil
}
示例#2
0
文件: rule.go 项目: micro/router-srv
func (r *Rule) Update(ctx context.Context, req *proto.UpdateRequest, rsp *proto.UpdateResponse) error {
	if req.Rule == nil {
		return errors.BadRequest("go.micro.srv.router.Rule.Update", "invalid rule")
	}

	if len(req.Rule.Id) == 0 {
		return errors.BadRequest("go.micro.srv.router.Rule.Update", "invalid id")
	}

	if len(req.Rule.Service) == 0 {
		return errors.BadRequest("go.micro.srv.router.Rule.Update", "invalid service")
	}

	if len(req.Rule.Version) == 0 {
		return errors.BadRequest("go.micro.srv.router.Rule.Update", "invalid version")
	}

	if req.Rule.Weight < 0 || req.Rule.Weight > 100 {
		return errors.BadRequest("go.micro.srv.router.Rule.Update", "invalid weight, must be 0 to 100")
	}

	if err := rule.Update(req.Rule); err != nil {
		return errors.InternalServerError("go.micro.srv.router.Rule.Update", err.Error())
	}

	return nil
}
示例#3
0
文件: label.go 项目: micro/router-srv
func (r *Label) Update(ctx context.Context, req *proto.UpdateRequest, rsp *proto.UpdateResponse) error {
	if req.Label == nil {
		return errors.BadRequest("go.micro.srv.router.Label.Update", "invalid label")
	}

	if len(req.Label.Id) == 0 {
		return errors.BadRequest("go.micro.srv.router.Label.Update", "invalid id")
	}

	if len(req.Label.Service) == 0 {
		return errors.BadRequest("go.micro.srv.router.Label.Update", "invalid service")
	}

	if len(req.Label.Key) == 0 {
		return errors.BadRequest("go.micro.srv.router.Label.Update", "invalid key")
	}

	if req.Label.Weight < 0 || req.Label.Weight > 100 {
		return errors.BadRequest("go.micro.srv.router.Label.Update", "invalid weight, must be 0 to 100")
	}

	if err := label.Update(req.Label); err != nil {
		return errors.InternalServerError("go.micro.srv.router.Label.Update", err.Error())
	}

	return nil
}
示例#4
0
文件: oauth2.go 项目: micro/auth-srv
func (o *Oauth2) Revoke(ctx context.Context, req *oauth2.RevokeRequest, rsp *oauth2.RevokeResponse) error {
	// Who should be allowed to do this?

	if len(req.RefreshToken) > 0 {
		token, _, err := db.ReadRefresh(req.RefreshToken)
		if err != nil {
			if err == db.ErrNotFound {
				return errors.BadRequest("go.micro.srv.auth", "invalid_request")
			}
			return errors.InternalServerError("go.micro.srv.auth", "server_error")
		}

		if err := db.DeleteToken(req.AccessToken); err != nil {
			return errors.InternalServerError("go.micro.srv.auth", "server_error")
		}

		req.AccessToken = token.AccessToken
	}

	if len(req.AccessToken) == 0 {
		return errors.BadRequest("go.micro.srv.auth", "invalid_request")
	}

	if err := db.DeleteToken(req.AccessToken); err != nil {
		return errors.InternalServerError("go.micro.srv.auth", "server_error")
	}

	return nil
}
示例#5
0
// DocRefFieldsExists returns an error if DocRef struct has zero value
func DocRefFieldsExists(dr *proto.DocRef) error {
	if len(dr.Index) <= 0 {
		return errors.BadRequest("go.micro.srv.elastic", "Index required")
	}

	if len(dr.Type) <= 0 {
		return errors.BadRequest("go.micro.srv.elastic", "Type required")
	}

	return nil
}
示例#6
0
// Query API handler
func (es *Elastic) Query(ctx context.Context, req *api.Request, rsp *api.Response) error {
	var err error
	var input map[string]interface{}
	var query []byte

	// Unmarshal unknown JSON
	if err = json.Unmarshal([]byte(req.Body), &input); err != nil {
		return errors.BadRequest("go.micro.api.elastic", err.Error())
	}

	query, err = json.Marshal(input["query"])

	srvReq := client.NewRequest(
		"go.micro.srv.elastic",
		"Elastic.Query",
		&elastic.QueryRequest{
			Index: fmt.Sprintf("%v", input["index"]),
			Type:  fmt.Sprintf("%v", input["type"]),
			Query: string(query),
		},
	)
	srvRsp := &elastic.SearchResponse{}
	if err = client.Call(ctx, srvReq, srvRsp); err != nil {
		return err
	}

	rsp.StatusCode = http.StatusOK
	rsp.Body = srvRsp.Result

	return nil
}
示例#7
0
文件: handler.go 项目: micro/db-srv
func validateDB(method string, d *mdb.Database) error {
	if d == nil {
		return errors.BadRequest("go.micro.srv.db."+method, "invalid database")
	}

	if len(d.Name) == 0 {
		return errors.BadRequest("go.micro.srv.db."+method, "database is blank")
	}
	if len(d.Table) == 0 {
		return errors.BadRequest("go.micro.srv.db."+method, "table is blank")
	}

	// TODO: check exists

	return nil
}
示例#8
0
文件: api.go 项目: yonglehou/micro
func (s *Say) Hello(ctx context.Context, req *api.Request, rsp *api.Response) error {
	log.Info("Received Say.Hello API request")

	name, ok := req.Get["name"]
	if !ok || len(name.Values) == 0 {
		return errors.BadRequest("go.micro.api.greeter", "Name cannot be blank")
	}

	request := client.NewRequest("go.micro.srv.greeter", "Say.Hello", &hello.Request{
		Name: strings.Join(name.Values, " "),
	})

	response := &hello.Response{}

	if err := client.Call(ctx, request, response); err != nil {
		return err
	}

	rsp.StatusCode = 200
	b, _ := json.Marshal(map[string]string{
		"message": response.Msg,
	})
	rsp.Body = string(b)

	return nil
}
示例#9
0
func (r *Router) SelectStream(ctx context.Context, req *proto.SelectRequest, stream proto.Router_SelectStreamStream) error {
	// TODO: process filters

	if len(req.Service) == 0 {
		return errors.BadRequest("go.micro.srv.router.Router.Select", "invalid service name")
	}

	t := time.NewTicker(time.Duration(router.DefaultExpiry) * time.Second)
	defer t.Stop()

	for {
		services, err := router.Select(req.Service)
		if err != nil && err == selector.ErrNotFound {
			return errors.NotFound("go.micro.srv.router.Router.SelectStream", err.Error())
		} else if err != nil {
			return errors.InternalServerError("go.micro.srv.router.Router.SelectStream", err.Error())
		}

		if err := stream.Send(&proto.SelectResponse{
			Services: services,
			Expires:  time.Now().Unix() + int64(router.DefaultExpiry),
		}); err != nil {
			return err
		}

		<-t.C
	}

	return nil
}
示例#10
0
文件: oauth2.go 项目: micro/auth-srv
func (o *Oauth2) Introspect(ctx context.Context, req *oauth2.IntrospectRequest, rsp *oauth2.IntrospectResponse) error {
	// Who should be allowed to do this?

	if len(req.AccessToken) == 0 {
		return errors.BadRequest("go.micro.srv.auth", "invalid_request")
	}

	token, _, err := db.ReadToken(req.AccessToken)
	if err != nil {
		if err == db.ErrNotFound {
			rsp.Active = false
			return nil
		}
		return errors.InternalServerError("go.micro.srv.auth", "server_error")
	}

	if d := time.Now().Unix() - token.ExpiresAt; d > 0 {
		rsp.Active = false
		return nil
	}

	rsp.Token = token
	rsp.Active = true
	// should we really hand this over?
	rsp.Token.RefreshToken = ""
	return nil
}
示例#11
0
// Update API handler
func (es *Elastic) Update(ctx context.Context, req *api.Request, rsp *api.Response) error {
	var err error
	var input map[string]interface{}
	var data []byte

	// Unmarshal unknown JSON
	if err = json.Unmarshal([]byte(req.Body), &input); err != nil {
		return errors.BadRequest("go.micro.api.elastic", err.Error())
	}

	// Marshal unknown JSON (data)
	data, err = json.Marshal(input["data"])

	srvReq := client.NewRequest(
		"go.micro.srv.elastic",
		"Elastic.Update",
		&elastic.UpdateRequest{
			Index: fmt.Sprintf("%v", input["index"]),
			Type:  fmt.Sprintf("%v", input["type"]),
			Id:    fmt.Sprintf("%v", input["id"]),
			Data:  string(data),
		},
	)
	srvRsp := &elastic.UpdateResponse{}
	if err = client.Call(ctx, srvReq, srvRsp); err != nil {
		return err
	}

	rsp.StatusCode = http.StatusOK
	rsp.Body = `{}`

	return nil
}
示例#12
0
文件: oauth2.go 项目: micro/auth-srv
func (o *Oauth2) Authorize(ctx context.Context, req *oauth2.AuthorizeRequest, rsp *oauth2.AuthorizeResponse) error {
	// We may actually need to authenticate who can make this request.
	// How should we do that?

	switch req.ResponseType {
	// requesting authorization code
	case "code":
		// check client id exists
		if len(req.ClientId) == 0 {
			return errors.BadRequest("go.micro.srv.auth", "invalid_request")
		}

		// if redirect uri exists and is not tls lets bail
		if len(req.RedirectUri) > 0 && !strings.HasPrefix(req.RedirectUri, "https://") {
			return errors.BadRequest("go.micro.srv.auth", "invalid_request")
		}

		// use default scope
		if len(req.Scopes) == 0 {
			req.Scopes = append(req.Scopes, DefaultScope)
		}

		// generate code
		code := db.Code()

		// store request; expire in 10 mins
		if err := db.CreateRequest(code, req); err != nil {
			return errors.InternalServerError("go.micro.srv.auth", "server_error")
		}

		// respond

		rsp.Code = code
		rsp.State = req.State

		// we're done?!

	// implicit token request
	case "token":
		// to be implemented
		return errors.BadRequest("go.micro.srv.auth", "unsupported_response_type")
	default:
		return errors.BadRequest("go.micro.srv.auth", "unsupported_response_type")
	}

	return nil
}
示例#13
0
文件: event.go 项目: micro/event-srv
func Stream(ctx context.Context, rec *event.Record) error {
	if rec == nil {
		return errors.BadRequest("go.micro.srv.event.Process", "invalid record")
	}

	if len(rec.Id) == 0 {
		return errors.BadRequest("go.micro.srv.event.Process", "invalid id")
	}

	if len(rec.Type) == 0 {
		return errors.BadRequest("go.micro.srv.event.Process", "invalid type")
	}

	if rec.Timestamp == 0 {
		return errors.BadRequest("go.micro.srv.event.Process", "invalid timestamp")
	}

	mtx.RLock()
	defer mtx.RUnlock()

	gsubscribers := subs[all]
	subscribers := subs[rec.Type]

	// send to subscribers in a go channel
	go func() {
		// send to global subscribers
		for _, sub := range gsubscribers {
			select {
			case sub.ch <- rec:
			case <-time.After(time.Millisecond * 100):
			}
		}

		// send to type subscribers
		for _, sub := range subscribers {
			select {
			case sub.ch <- rec:
			case <-time.After(time.Millisecond * 100):
			}
		}
	}()

	return nil
}
示例#14
0
func (e *Event) Update(ctx context.Context, req *event.UpdateRequest, rsp *event.UpdateResponse) error {
	if req.Record == nil {
		return errors.BadRequest("go.micro.srv.event.Update", "invalid record")
	}

	if len(req.Record.Id) == 0 {
		return errors.BadRequest("go.micro.srv.event.Update", "invalid id")
	}

	if req.Record.Timestamp == 0 {
		req.Record.Timestamp = time.Now().Unix()
	}

	if err := db.Update(req.Record); err != nil {
		return errors.InternalServerError("go.micro.srv.event.Update", err.Error())
	}

	return nil
}
示例#15
0
文件: account.go 项目: micro/auth-srv
func (s *Account) Delete(ctx context.Context, req *account.DeleteRequest, rsp *account.DeleteResponse) error {
	if len(req.Id) == 0 {
		return errors.BadRequest("go.micro.srv.auth.Delete", "invalid id")
	}

	if err := db.Delete(req.Id); err != nil {
		return errors.InternalServerError("go.micro.srv.auth.Delete", err.Error())
	}
	return nil
}
示例#16
0
文件: handler.go 项目: micro/db-srv
func (d *DB) Create(ctx context.Context, req *mdb.CreateRequest, rsp *mdb.CreateResponse) error {
	if req.Record == nil {
		return errors.BadRequest("go.micro.srv.db.DB.Create", "invalid record")
	}

	if err := validateDB("DB.Create", req.Database); err != nil {
		return err
	}

	if len(req.Record.Id) == 0 {
		return errors.BadRequest("go.micro.srv.db.DB.Create", "invalid id")
	}

	if err := db.Create(req.Database, req.Record); err != nil {
		return errors.InternalServerError("go.micro.srv.db.DB.Create", err.Error())
	}

	return nil
}
示例#17
0
文件: account.go 项目: micro/auth-srv
func (s *Account) Update(ctx context.Context, req *account.UpdateRequest, rsp *account.UpdateResponse) error {
	// validate incoming
	if err := validateAccount(req.Account, "Update"); err != nil {
		return err
	}

	// need an account id for update
	if len(req.Account.Id) == 0 {
		return errors.BadRequest("go.micro.srv.auth.Update", "invalid id")
	}

	// lookup the record and verify it's the same
	acc, err := db.Read(req.Account.Id)
	if err != nil {
		return errors.InternalServerError("go.micro.srv.auth.Update", err.Error())
	}

	// not the same client id
	if req.Account.ClientId != acc.ClientId {
		return errors.BadRequest("go.micro.srv.auth.Update", "invalid client id")
	}

	// hash the pass
	salt := db.Salt()
	h, err := bcrypt.GenerateFromPassword([]byte(x+salt+req.Account.ClientSecret), 10)
	if err != nil {
		return errors.InternalServerError("go.micro.srv.auth.Update", err.Error())
	}
	pp := base64.StdEncoding.EncodeToString(h)

	// to lower
	req.Account.ClientId = strings.ToLower(req.Account.ClientId)
	req.Account.Type = strings.ToLower(req.Account.Type)

	// update
	if err := db.Update(req.Account, salt, pp); err != nil {
		return errors.InternalServerError("go.micro.srv.auth.Update", err.Error())
	}

	return nil
}
示例#18
0
文件: account.go 项目: micro/auth-srv
func (s *Account) Read(ctx context.Context, req *account.ReadRequest, rsp *account.ReadResponse) error {
	if len(req.Id) == 0 {
		return errors.BadRequest("go.micro.srv.auth.Read", "id cannot be blank")
	}

	acc, err := db.Read(req.Id)
	if err != nil {
		return errors.InternalServerError("go.micro.srv.auth.Read", err.Error())
	}

	rsp.Account = acc
	return nil
}
示例#19
0
文件: label.go 项目: micro/router-srv
func (r *Label) Delete(ctx context.Context, req *proto.DeleteRequest, rsp *proto.DeleteResponse) error {
	if len(req.Id) == 0 {
		return errors.BadRequest("go.micro.srv.router.Label.Delete", "invalid id")
	}

	if err := label.Delete(req.Id); err != nil {
		if err == db.ErrNotFound {
			return nil
		}
		return errors.InternalServerError("go.micro.srv.router.Label.Delete", err.Error())
	}

	return nil
}
示例#20
0
文件: event.go 项目: micro/event-srv
func Process(ctx context.Context, rec *event.Record) error {
	if rec == nil {
		return errors.BadRequest("go.micro.srv.event.Process", "invalid record")
	}

	if len(rec.Id) == 0 {
		return errors.BadRequest("go.micro.srv.event.Process", "invalid id")
	}

	if len(rec.Type) == 0 {
		return errors.BadRequest("go.micro.srv.event.Process", "invalid type")
	}

	if rec.Timestamp == 0 {
		return errors.BadRequest("go.micro.srv.event.Process", "invalid timestamp")
	}

	if err := db.Create(rec); err != nil {
		return errors.InternalServerError("go.micro.srv.event.Process", err.Error())
	}

	return nil
}
示例#21
0
文件: account.go 项目: micro/auth-srv
func validateAccount(acc *account.Record, method string) error {
	if acc == nil {
		return errors.BadRequest("go.micro.srv.auth."+method, "invalid account")
	}

	if len(acc.Id) > 0 && uuid.Parse(acc.Id) == nil {
		return errors.BadRequest("go.micro.srv.auth."+method, "invalid id")
	}

	if len(acc.Type) == 0 {
		return errors.BadRequest("go.micro.srv.auth."+method, "type cannot be blank")
	}

	if len(acc.ClientId) == 0 {
		return errors.BadRequest("go.micro.srv.auth."+method, "client id cannot be blank")
	}

	if len(acc.ClientSecret) == 0 {
		return errors.BadRequest("go.micro.srv.auth."+method, "client secret cannot be blank")
	}

	return nil
}
示例#22
0
func (r *Router) Stats(ctx context.Context, req *proto.StatsRequest, rsp *proto.StatsResponse) error {
	if len(req.Service) == 0 {
		return errors.BadRequest("go.micro.srv.router.Router.Stats", "invalid service name")
	}

	stats, err := router.Stats(req.Service, req.NodeId)
	if err != nil {
		return errors.InternalServerError("go.micro.srv.router.Router.Stats", err.Error())
	}

	rsp.Stats = stats

	return nil
}
示例#23
0
func (e *Event) Read(ctx context.Context, req *event.ReadRequest, rsp *event.ReadResponse) error {
	if len(req.Id) == 0 {
		return errors.BadRequest("go.micro.srv.event.Read", "invalid id")
	}

	rec, err := db.Read(req.Id)
	if err != nil {
		return errors.InternalServerError("go.micro.srv.event.Update", err.Error())
	}

	rsp.Record = rec

	return nil
}
示例#24
0
func (h *httpBroker) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	if req.Method != "POST" {
		err := errors.BadRequest("go.micro.broker", "Method not allowed")
		http.Error(w, err.Error(), http.StatusMethodNotAllowed)
		return
	}
	defer req.Body.Close()

	req.ParseForm()

	b, err := ioutil.ReadAll(req.Body)
	if err != nil {
		errr := errors.InternalServerError("go.micro.broker", fmt.Sprintf("Error reading request body: %v", err))
		w.WriteHeader(500)
		w.Write([]byte(errr.Error()))
		return
	}

	var m *Message
	if err = json.Unmarshal(b, &m); err != nil {
		errr := errors.InternalServerError("go.micro.broker", fmt.Sprintf("Error parsing request body: %v", err))
		w.WriteHeader(500)
		w.Write([]byte(errr.Error()))
		return
	}

	topic := m.Header[":topic"]
	delete(m.Header, ":topic")

	if len(topic) == 0 {
		errr := errors.InternalServerError("go.micro.broker", "Topic not found")
		w.WriteHeader(500)
		w.Write([]byte(errr.Error()))
		return
	}

	p := &httpPublication{m: m, t: topic}
	id := req.Form.Get("id")

	h.RLock()
	for _, subscriber := range h.subscribers[topic] {
		if id == subscriber.id {
			// sub is sync; crufty rate limiting
			// so we don't hose the cpu
			subscriber.fn(p)
		}
	}
	h.RUnlock()
}
示例#25
0
文件: label.go 项目: micro/router-srv
func (r *Label) Read(ctx context.Context, req *proto.ReadRequest, rsp *proto.ReadResponse) error {
	if len(req.Id) == 0 {
		return errors.BadRequest("go.micro.srv.router.Label.Read", "invalid id")
	}

	l, err := label.Read(req.Id)
	if err != nil {
		if err == db.ErrNotFound {
			return errors.NotFound("go.micro.srv.router.Label.Read", err.Error())
		}
		return errors.InternalServerError("go.micro.srv.router.Label.Read", err.Error())
	}

	rsp.Label = l

	return nil
}
示例#26
0
文件: handler.go 项目: micro/db-srv
func (d *DB) Delete(ctx context.Context, req *mdb.DeleteRequest, rsp *mdb.DeleteResponse) error {
	if err := validateDB("DB.Delete", req.Database); err != nil {
		return err
	}

	if len(req.Id) == 0 {
		return errors.BadRequest("go.micro.srv.db.DB.Delete", "invalid id")
	}

	if err := db.Delete(req.Database, req.Id); err != nil && err == db.ErrNotFound {
		return nil
	} else if err != nil {
		return errors.InternalServerError("go.micro.srv.db.DB.Delete", err.Error())
	}

	return nil
}
示例#27
0
func (r *Router) Select(ctx context.Context, req *proto.SelectRequest, rsp *proto.SelectResponse) error {
	// TODO: process filters

	if len(req.Service) == 0 {
		return errors.BadRequest("go.micro.srv.router.Router.Select", "invalid service name")
	}

	services, err := router.Select(req.Service)
	if err != nil && err == selector.ErrNotFound {
		return errors.NotFound("go.micro.srv.router.Router.Select", err.Error())
	} else if err != nil {
		return errors.InternalServerError("go.micro.srv.router.Router.Select", err.Error())
	}

	rsp.Services = services
	rsp.Expires = time.Now().Unix() + int64(router.DefaultExpiry)

	return nil
}
示例#28
0
文件: handler.go 项目: micro/db-srv
func (d *DB) Read(ctx context.Context, req *mdb.ReadRequest, rsp *mdb.ReadResponse) error {
	if err := validateDB("DB.Read", req.Database); err != nil {
		return err
	}

	if len(req.Id) == 0 {
		return errors.BadRequest("go.micro.srv.db.DB.Read", "invalid id")
	}

	r, err := db.Read(req.Database, req.Id)
	if err != nil && err == db.ErrNotFound {
		return errors.NotFound("go.micro.srv.db.DB.Read", "not found")
	} else if err != nil {
		return errors.InternalServerError("go.micro.srv.db.DB.Read", err.Error())
	}

	rsp.Record = r

	return nil
}
示例#29
0
func (c *Config) Read(ctx context.Context, req *proto.ReadRequest, rsp *proto.ReadResponse) error {
	if len(req.Id) == 0 {
		return errors.BadRequest("go.micro.srv.config.Read", "invalid id")
	}

	ch, err := db.Read(req.Id)
	if err != nil {
		return errors.InternalServerError("go.micro.srv.config.Read", err.Error())
	}

	// Set response
	rsp.Change = ch

	if len(req.Path) == 0 {
		rsp.Change.Path = ""
		rsp.Change.Timestamp = 0
		return nil
	}

	rsp.Change.Path = req.Path

	values, err := config.Values(&conf.ChangeSet{
		Timestamp: time.Unix(ch.ChangeSet.Timestamp, 0),
		Data:      []byte(ch.ChangeSet.Data),
		Checksum:  ch.ChangeSet.Checksum,
		Source:    ch.ChangeSet.Source,
	})
	if err != nil {
		return errors.InternalServerError("go.micro.srv.config.Read", err.Error())
	}

	parts := strings.Split(req.Path, config.PathSplitter)

	// we just want to pass back bytes
	rsp.Change.ChangeSet.Data = string(values.Get(parts...).Bytes())

	return nil
}
示例#30
0
func (c *Config) Watch(ctx context.Context, req *proto.WatchRequest, stream proto.Config_WatchStream) error {
	if len(req.Id) == 0 {
		return errors.BadRequest("go.micro.srv.config.Watch", "invalid id")
	}

	watch, err := config.Watch(req.Id)
	if err != nil {
		return errors.InternalServerError("go.micro.srv.config.Watch", err.Error())
	}
	defer watch.Stop()

	for {
		ch, err := watch.Next()
		if err != nil {
			stream.Close()
			return errors.InternalServerError("go.micro.srv.config.Watch", err.Error())
		}

		if err := stream.Send(ch); err != nil {
			stream.Close()
			return errors.InternalServerError("go.micro.srv.config.Watch", err.Error())
		}
	}
}