fix: issues
This commit is contained in:
174
backend/app/consts/consts.gen.go
Normal file
174
backend/app/consts/consts.gen.go
Normal file
@@ -0,0 +1,174 @@
|
||||
// Code generated by go-enum DO NOT EDIT.
|
||||
// Version: -
|
||||
// Revision: -
|
||||
// Build Date: -
|
||||
// Built By: -
|
||||
|
||||
package consts
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
// TokenTypeUser is a TokenType of type User.
|
||||
TokenTypeUser TokenType = "__tu"
|
||||
// TokenTypeTenant is a TokenType of type Tenant.
|
||||
TokenTypeTenant TokenType = "__tt"
|
||||
)
|
||||
|
||||
var ErrInvalidTokenType = fmt.Errorf("not a valid TokenType, try [%s]", strings.Join(_TokenTypeNames, ", "))
|
||||
|
||||
var _TokenTypeNames = []string{
|
||||
string(TokenTypeUser),
|
||||
string(TokenTypeTenant),
|
||||
}
|
||||
|
||||
// TokenTypeNames returns a list of possible string values of TokenType.
|
||||
func TokenTypeNames() []string {
|
||||
tmp := make([]string, len(_TokenTypeNames))
|
||||
copy(tmp, _TokenTypeNames)
|
||||
return tmp
|
||||
}
|
||||
|
||||
// TokenTypeValues returns a list of the values for TokenType
|
||||
func TokenTypeValues() []TokenType {
|
||||
return []TokenType{
|
||||
TokenTypeUser,
|
||||
TokenTypeTenant,
|
||||
}
|
||||
}
|
||||
|
||||
// String implements the Stringer interface.
|
||||
func (x TokenType) String() string {
|
||||
return string(x)
|
||||
}
|
||||
|
||||
// IsValid provides a quick way to determine if the typed value is
|
||||
// part of the allowed enumerated values
|
||||
func (x TokenType) IsValid() bool {
|
||||
_, err := ParseTokenType(string(x))
|
||||
return err == nil
|
||||
}
|
||||
|
||||
var _TokenTypeValue = map[string]TokenType{
|
||||
"__tu": TokenTypeUser,
|
||||
"__tt": TokenTypeTenant,
|
||||
}
|
||||
|
||||
// ParseTokenType attempts to convert a string to a TokenType.
|
||||
func ParseTokenType(name string) (TokenType, error) {
|
||||
if x, ok := _TokenTypeValue[name]; ok {
|
||||
return x, nil
|
||||
}
|
||||
return TokenType(""), fmt.Errorf("%s is %w", name, ErrInvalidTokenType)
|
||||
}
|
||||
|
||||
var errTokenTypeNilPtr = errors.New("value pointer is nil") // one per type for package clashes
|
||||
|
||||
// Scan implements the Scanner interface.
|
||||
func (x *TokenType) Scan(value interface{}) (err error) {
|
||||
if value == nil {
|
||||
*x = TokenType("")
|
||||
return
|
||||
}
|
||||
|
||||
// A wider range of scannable types.
|
||||
// driver.Value values at the top of the list for expediency
|
||||
switch v := value.(type) {
|
||||
case string:
|
||||
*x, err = ParseTokenType(v)
|
||||
case []byte:
|
||||
*x, err = ParseTokenType(string(v))
|
||||
case TokenType:
|
||||
*x = v
|
||||
case *TokenType:
|
||||
if v == nil {
|
||||
return errTokenTypeNilPtr
|
||||
}
|
||||
*x = *v
|
||||
case *string:
|
||||
if v == nil {
|
||||
return errTokenTypeNilPtr
|
||||
}
|
||||
*x, err = ParseTokenType(*v)
|
||||
default:
|
||||
return errors.New("invalid type for TokenType")
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Value implements the driver Valuer interface.
|
||||
func (x TokenType) Value() (driver.Value, error) {
|
||||
return x.String(), nil
|
||||
}
|
||||
|
||||
// Set implements the Golang flag.Value interface func.
|
||||
func (x *TokenType) Set(val string) error {
|
||||
v, err := ParseTokenType(val)
|
||||
*x = v
|
||||
return err
|
||||
}
|
||||
|
||||
// Get implements the Golang flag.Getter interface func.
|
||||
func (x *TokenType) Get() interface{} {
|
||||
return *x
|
||||
}
|
||||
|
||||
// Type implements the github.com/spf13/pFlag Value interface.
|
||||
func (x *TokenType) Type() string {
|
||||
return "TokenType"
|
||||
}
|
||||
|
||||
type NullTokenType struct {
|
||||
TokenType TokenType
|
||||
Valid bool
|
||||
}
|
||||
|
||||
func NewNullTokenType(val interface{}) (x NullTokenType) {
|
||||
err := x.Scan(val) // yes, we ignore this error, it will just be an invalid value.
|
||||
_ = err // make any errcheck linters happy
|
||||
return
|
||||
}
|
||||
|
||||
// Scan implements the Scanner interface.
|
||||
func (x *NullTokenType) Scan(value interface{}) (err error) {
|
||||
if value == nil {
|
||||
x.TokenType, x.Valid = TokenType(""), false
|
||||
return
|
||||
}
|
||||
|
||||
err = x.TokenType.Scan(value)
|
||||
x.Valid = (err == nil)
|
||||
return
|
||||
}
|
||||
|
||||
// Value implements the driver Valuer interface.
|
||||
func (x NullTokenType) Value() (driver.Value, error) {
|
||||
if !x.Valid {
|
||||
return nil, nil
|
||||
}
|
||||
// driver.Value accepts int64 for int values.
|
||||
return string(x.TokenType), nil
|
||||
}
|
||||
|
||||
type NullTokenTypeStr struct {
|
||||
NullTokenType
|
||||
}
|
||||
|
||||
func NewNullTokenTypeStr(val interface{}) (x NullTokenTypeStr) {
|
||||
x.Scan(val) // yes, we ignore this error, it will just be an invalid value.
|
||||
return
|
||||
}
|
||||
|
||||
// Value implements the driver Valuer interface.
|
||||
func (x NullTokenTypeStr) Value() (driver.Value, error) {
|
||||
if !x.Valid {
|
||||
return nil, nil
|
||||
}
|
||||
return x.TokenType.String(), nil
|
||||
}
|
||||
7
backend/app/consts/consts.go
Normal file
7
backend/app/consts/consts.go
Normal file
@@ -0,0 +1,7 @@
|
||||
package consts
|
||||
|
||||
// swagger:enum TokenType
|
||||
// ENUM(
|
||||
// User = "__tu", Tenant = "__tt"
|
||||
// )
|
||||
type TokenType string
|
||||
@@ -1,71 +0,0 @@
|
||||
package events
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"git.ipao.vip/rogeecn/atom/contracts"
|
||||
"github.com/ThreeDotsLabs/watermill"
|
||||
"github.com/ThreeDotsLabs/watermill/message"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
var _ contracts.EventHandler = (*UserRegister)(nil)
|
||||
|
||||
type Event struct {
|
||||
ID int `json:"id"`
|
||||
}
|
||||
|
||||
type ProcessedEvent struct {
|
||||
ProcessedID int `json:"processed_id"`
|
||||
Time time.Time `json:"time"`
|
||||
}
|
||||
|
||||
// @provider(event)
|
||||
type UserRegister struct {
|
||||
log *logrus.Entry `inject:"false"`
|
||||
}
|
||||
|
||||
func (u *UserRegister) Prepare() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Handler implements contracts.EventHandler.
|
||||
func (u *UserRegister) Handler(msg *message.Message) ([]*message.Message, error) {
|
||||
consumedPayload := Event{}
|
||||
err := json.Unmarshal(msg.Payload, &consumedPayload)
|
||||
if err != nil {
|
||||
// When a handler returns an error, the default behavior is to send a Nack (negative-acknowledgement).
|
||||
// The message will be processed again.
|
||||
//
|
||||
// You can change the default behaviour by using middlewares, like Retry or PoisonQueue.
|
||||
// You can also implement your own middleware.
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fmt.Printf("received event %+v\n", consumedPayload)
|
||||
|
||||
newPayload, err := json.Marshal(ProcessedEvent{
|
||||
ProcessedID: consumedPayload.ID,
|
||||
Time: time.Now(),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
newMessage := message.NewMessage(watermill.NewUUID(), newPayload)
|
||||
|
||||
return nil, nil
|
||||
return []*message.Message{newMessage}, nil
|
||||
}
|
||||
|
||||
// PublishToTopic implements contracts.EventHandler.
|
||||
func (u *UserRegister) PublishToTopic() string {
|
||||
return "event:processed"
|
||||
}
|
||||
|
||||
// Topic implements contracts.EventHandler.
|
||||
func (u *UserRegister) Topic() string {
|
||||
return "event:user-register"
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
package events
|
||||
|
||||
import (
|
||||
"backend/providers/events"
|
||||
"backend/providers/event"
|
||||
|
||||
"git.ipao.vip/rogeecn/atom"
|
||||
"git.ipao.vip/rogeecn/atom/container"
|
||||
@@ -11,7 +11,7 @@ import (
|
||||
|
||||
func Provide(opts ...opt.Option) error {
|
||||
if err := container.Container.Provide(func(
|
||||
__event *events.PubSub,
|
||||
__event *event.PubSub,
|
||||
) (contracts.Initial, error) {
|
||||
obj := &UserRegister{}
|
||||
if err := obj.Prepare(); err != nil {
|
||||
|
||||
174
backend/app/events/topics.gen.go
Normal file
174
backend/app/events/topics.gen.go
Normal file
@@ -0,0 +1,174 @@
|
||||
// Code generated by go-enum DO NOT EDIT.
|
||||
// Version: -
|
||||
// Revision: -
|
||||
// Build Date: -
|
||||
// Built By: -
|
||||
|
||||
package events
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
// TopicProcessed is a Topic of type Processed.
|
||||
TopicProcessed Topic = "event:processed"
|
||||
// TopicUserRegister is a Topic of type UserRegister.
|
||||
TopicUserRegister Topic = "user:register"
|
||||
)
|
||||
|
||||
var ErrInvalidTopic = fmt.Errorf("not a valid Topic, try [%s]", strings.Join(_TopicNames, ", "))
|
||||
|
||||
var _TopicNames = []string{
|
||||
string(TopicProcessed),
|
||||
string(TopicUserRegister),
|
||||
}
|
||||
|
||||
// TopicNames returns a list of possible string values of Topic.
|
||||
func TopicNames() []string {
|
||||
tmp := make([]string, len(_TopicNames))
|
||||
copy(tmp, _TopicNames)
|
||||
return tmp
|
||||
}
|
||||
|
||||
// TopicValues returns a list of the values for Topic
|
||||
func TopicValues() []Topic {
|
||||
return []Topic{
|
||||
TopicProcessed,
|
||||
TopicUserRegister,
|
||||
}
|
||||
}
|
||||
|
||||
// String implements the Stringer interface.
|
||||
func (x Topic) String() string {
|
||||
return string(x)
|
||||
}
|
||||
|
||||
// IsValid provides a quick way to determine if the typed value is
|
||||
// part of the allowed enumerated values
|
||||
func (x Topic) IsValid() bool {
|
||||
_, err := ParseTopic(string(x))
|
||||
return err == nil
|
||||
}
|
||||
|
||||
var _TopicValue = map[string]Topic{
|
||||
"event:processed": TopicProcessed,
|
||||
"user:register": TopicUserRegister,
|
||||
}
|
||||
|
||||
// ParseTopic attempts to convert a string to a Topic.
|
||||
func ParseTopic(name string) (Topic, error) {
|
||||
if x, ok := _TopicValue[name]; ok {
|
||||
return x, nil
|
||||
}
|
||||
return Topic(""), fmt.Errorf("%s is %w", name, ErrInvalidTopic)
|
||||
}
|
||||
|
||||
var errTopicNilPtr = errors.New("value pointer is nil") // one per type for package clashes
|
||||
|
||||
// Scan implements the Scanner interface.
|
||||
func (x *Topic) Scan(value interface{}) (err error) {
|
||||
if value == nil {
|
||||
*x = Topic("")
|
||||
return
|
||||
}
|
||||
|
||||
// A wider range of scannable types.
|
||||
// driver.Value values at the top of the list for expediency
|
||||
switch v := value.(type) {
|
||||
case string:
|
||||
*x, err = ParseTopic(v)
|
||||
case []byte:
|
||||
*x, err = ParseTopic(string(v))
|
||||
case Topic:
|
||||
*x = v
|
||||
case *Topic:
|
||||
if v == nil {
|
||||
return errTopicNilPtr
|
||||
}
|
||||
*x = *v
|
||||
case *string:
|
||||
if v == nil {
|
||||
return errTopicNilPtr
|
||||
}
|
||||
*x, err = ParseTopic(*v)
|
||||
default:
|
||||
return errors.New("invalid type for Topic")
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Value implements the driver Valuer interface.
|
||||
func (x Topic) Value() (driver.Value, error) {
|
||||
return x.String(), nil
|
||||
}
|
||||
|
||||
// Set implements the Golang flag.Value interface func.
|
||||
func (x *Topic) Set(val string) error {
|
||||
v, err := ParseTopic(val)
|
||||
*x = v
|
||||
return err
|
||||
}
|
||||
|
||||
// Get implements the Golang flag.Getter interface func.
|
||||
func (x *Topic) Get() interface{} {
|
||||
return *x
|
||||
}
|
||||
|
||||
// Type implements the github.com/spf13/pFlag Value interface.
|
||||
func (x *Topic) Type() string {
|
||||
return "Topic"
|
||||
}
|
||||
|
||||
type NullTopic struct {
|
||||
Topic Topic
|
||||
Valid bool
|
||||
}
|
||||
|
||||
func NewNullTopic(val interface{}) (x NullTopic) {
|
||||
err := x.Scan(val) // yes, we ignore this error, it will just be an invalid value.
|
||||
_ = err // make any errcheck linters happy
|
||||
return
|
||||
}
|
||||
|
||||
// Scan implements the Scanner interface.
|
||||
func (x *NullTopic) Scan(value interface{}) (err error) {
|
||||
if value == nil {
|
||||
x.Topic, x.Valid = Topic(""), false
|
||||
return
|
||||
}
|
||||
|
||||
err = x.Topic.Scan(value)
|
||||
x.Valid = (err == nil)
|
||||
return
|
||||
}
|
||||
|
||||
// Value implements the driver Valuer interface.
|
||||
func (x NullTopic) Value() (driver.Value, error) {
|
||||
if !x.Valid {
|
||||
return nil, nil
|
||||
}
|
||||
// driver.Value accepts int64 for int values.
|
||||
return string(x.Topic), nil
|
||||
}
|
||||
|
||||
type NullTopicStr struct {
|
||||
NullTopic
|
||||
}
|
||||
|
||||
func NewNullTopicStr(val interface{}) (x NullTopicStr) {
|
||||
x.Scan(val) // yes, we ignore this error, it will just be an invalid value.
|
||||
return
|
||||
}
|
||||
|
||||
// Value implements the driver Valuer interface.
|
||||
func (x NullTopicStr) Value() (driver.Value, error) {
|
||||
if !x.Valid {
|
||||
return nil, nil
|
||||
}
|
||||
return x.Topic.String(), nil
|
||||
}
|
||||
10
backend/app/events/topics.go
Normal file
10
backend/app/events/topics.go
Normal file
@@ -0,0 +1,10 @@
|
||||
package events
|
||||
|
||||
// swagger:enum Topic
|
||||
// ENUM(
|
||||
//
|
||||
// Processed = "event:processed"
|
||||
// UserRegister = "user:register"
|
||||
//
|
||||
// )
|
||||
type Topic string
|
||||
52
backend/app/events/user_register.go
Normal file
52
backend/app/events/user_register.go
Normal file
@@ -0,0 +1,52 @@
|
||||
package events
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"git.ipao.vip/rogeecn/atom/contracts"
|
||||
"github.com/ThreeDotsLabs/watermill/message"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
var (
|
||||
_ contracts.EventHandler = (*UserRegister)(nil)
|
||||
_ contracts.EventPublisher = (*UserRegister)(nil)
|
||||
)
|
||||
|
||||
// @provider(event)
|
||||
type UserRegister struct {
|
||||
log *logrus.Entry `inject:"false" json:"-"`
|
||||
ID int64 `json:"id"`
|
||||
}
|
||||
|
||||
func (e *UserRegister) Prepare() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Marshal implements contracts.EventPublisher.
|
||||
func (e *UserRegister) Marshal() ([]byte, error) {
|
||||
return json.Marshal(e)
|
||||
}
|
||||
|
||||
// PublishToTopic implements contracts.EventHandler.
|
||||
func (e *UserRegister) PublishToTopic() string {
|
||||
return TopicProcessed.String()
|
||||
}
|
||||
|
||||
// Topic implements contracts.EventHandler.
|
||||
func (e *UserRegister) Topic() string {
|
||||
return TopicUserRegister.String()
|
||||
}
|
||||
|
||||
// Handler implements contracts.EventHandler.
|
||||
func (e *UserRegister) Handler(msg *message.Message) ([]*message.Message, error) {
|
||||
var payload UserRegister
|
||||
err := json.Unmarshal(msg.Payload, &payload)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
e.log.Infof("received event %+v\n", payload)
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
120
backend/app/http/auth/controller_wechat.go
Normal file
120
backend/app/http/auth/controller_wechat.go
Normal file
@@ -0,0 +1,120 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"backend/app/consts"
|
||||
"backend/app/http/users"
|
||||
"backend/providers/jwt"
|
||||
"backend/providers/otel"
|
||||
"backend/providers/wechat"
|
||||
|
||||
"github.com/gofiber/fiber/v3"
|
||||
"github.com/pkg/errors"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
const StatePrefix = "sns_basic_auth"
|
||||
|
||||
// @provider
|
||||
type Controller struct {
|
||||
svc *Service
|
||||
userSvc *users.Service
|
||||
jwt *jwt.JWT
|
||||
wechat *wechat.Client
|
||||
log *log.Entry `inject:"false"`
|
||||
}
|
||||
|
||||
func (ctl *Controller) Prepare() error {
|
||||
ctl.log = log.WithField("module", "auth.Controller")
|
||||
return nil
|
||||
}
|
||||
|
||||
// @Router /v1/auth/wechat/jump/:tenant [get]
|
||||
// @Bind tenant path
|
||||
// @Bind redirectUri query
|
||||
func (ctl *Controller) JumpToAuth(ctx fiber.Ctx, tenant, redirectUri string) error {
|
||||
_, span := otel.Start(ctx.Context(), "auth.controller.wechat")
|
||||
defer span.End()
|
||||
|
||||
ctl.log.Debugf("%s, query: %v", ctx.OriginalURL(), ctx.Queries())
|
||||
|
||||
paramRedirect := ctx.Query("redirect")
|
||||
|
||||
// 添加 redirect 参数
|
||||
u, err := url.Parse(string(ctx.Request().URI().FullURI()))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
query := u.Query()
|
||||
query.Set("redirect", paramRedirect)
|
||||
u.RawQuery = query.Encode()
|
||||
u.Path = "/v1/auth/wechat/login/" + tenant
|
||||
fullUrl := u.String()
|
||||
|
||||
ctl.log.WithField("module", "middleware.SilentAuth").Debug("redirect_uri: ", fullUrl)
|
||||
|
||||
to, err := ctl.wechat.ScopeAuthorizeURL(
|
||||
wechat.ScopeAuthorizeURLWithRedirectURI(fullUrl),
|
||||
wechat.ScopeAuthorizeURLWithState(fmt.Sprintf("%s-%d", StatePrefix, time.Now().UnixNano())),
|
||||
)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to get wechat auth url")
|
||||
}
|
||||
|
||||
return ctx.Redirect().To(to.String())
|
||||
}
|
||||
|
||||
// @Router /v1/auth/login/:tenant [get]
|
||||
// @Bind tenant path
|
||||
// @Bind code query
|
||||
// @Bind state query
|
||||
// @Bind redirectUri query
|
||||
func (ctl *Controller) Login(ctx fiber.Ctx, code, state, tenant, redirectUri string) error {
|
||||
ctl.log.Debugf("code: %s, state: %s", code, state)
|
||||
|
||||
ctx.Cookie(&fiber.Cookie{
|
||||
Name: consts.TokenTypeUser.String(),
|
||||
Value: "",
|
||||
Expires: time.Now().Add(12 * time.Hour),
|
||||
HTTPOnly: true,
|
||||
})
|
||||
|
||||
// get the openid
|
||||
token, err := ctl.wechat.AuthorizeCode2Token(code)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to get openid")
|
||||
}
|
||||
ctl.log.Debugf("tokenInfo %+v", token)
|
||||
|
||||
user, err := ctl.userSvc.GetOrNewFromChannel(ctx.Context(), consts.AuthChannelWeChat, token.OpenID, tenant)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to get user")
|
||||
}
|
||||
|
||||
claim := c.jwt.CreateClaims(jwt.BaseClaims{
|
||||
OpenID: user.OpenID,
|
||||
Tenant: tenantSlug,
|
||||
UserID: user.ID,
|
||||
TenantID: tenant.ID,
|
||||
})
|
||||
jwtToken, err = c.jwt.CreateToken(claim)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to create token")
|
||||
}
|
||||
|
||||
ctx.Cookie(&fiber.Cookie{
|
||||
Name: "token",
|
||||
Value: jwtToken,
|
||||
Expires: time.Now().Add(6 * time.Hour),
|
||||
HTTPOnly: true,
|
||||
})
|
||||
|
||||
html := strings.ReplaceAll(string(b), "{{JWT}}", jwtToken)
|
||||
return ctx.SendString(html)
|
||||
|
||||
return ctx.Redirect().To(paramRedirect)
|
||||
}
|
||||
1
backend/app/http/auth/dto.go
Normal file
1
backend/app/http/auth/dto.go
Normal file
@@ -0,0 +1 @@
|
||||
package auth
|
||||
50
backend/app/http/auth/provider.gen.go
Executable file
50
backend/app/http/auth/provider.gen.go
Executable file
@@ -0,0 +1,50 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
|
||||
"backend/app/http/users"
|
||||
"backend/providers/jwt"
|
||||
"backend/providers/wechat"
|
||||
|
||||
"git.ipao.vip/rogeecn/atom/container"
|
||||
"git.ipao.vip/rogeecn/atom/utils/opt"
|
||||
)
|
||||
|
||||
func Provide(opts ...opt.Option) error {
|
||||
if err := container.Container.Provide(func(
|
||||
jwt *jwt.JWT,
|
||||
svc *Service,
|
||||
userSvc *users.Service,
|
||||
wechat *wechat.Client,
|
||||
) (*Controller, error) {
|
||||
obj := &Controller{
|
||||
jwt: jwt,
|
||||
svc: svc,
|
||||
userSvc: userSvc,
|
||||
wechat: wechat,
|
||||
}
|
||||
if err := obj.Prepare(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return obj, nil
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := container.Container.Provide(func(
|
||||
db *sql.DB,
|
||||
) (*Service, error) {
|
||||
obj := &Service{
|
||||
db: db,
|
||||
}
|
||||
if err := obj.Prepare(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return obj, nil
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
20
backend/app/http/auth/service.go
Normal file
20
backend/app/http/auth/service.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
|
||||
. "github.com/go-jet/jet/v2/postgres"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// @provider:except
|
||||
type Service struct {
|
||||
db *sql.DB
|
||||
log *log.Entry `inject:"false"`
|
||||
}
|
||||
|
||||
func (svc *Service) Prepare() error {
|
||||
svc.log = log.WithField("module", "auth.service")
|
||||
_ = Int(1)
|
||||
return nil
|
||||
}
|
||||
37
backend/app/http/auth/service_test.go
Normal file
37
backend/app/http/auth/service_test.go
Normal file
@@ -0,0 +1,37 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"backend/app/service/testx"
|
||||
|
||||
. "github.com/smartystreets/goconvey/convey"
|
||||
"github.com/stretchr/testify/suite"
|
||||
"go.uber.org/dig"
|
||||
)
|
||||
|
||||
type ServiceInjectParams struct {
|
||||
dig.In
|
||||
Svc *Service
|
||||
}
|
||||
|
||||
type ServiceTestSuite struct {
|
||||
suite.Suite
|
||||
ServiceInjectParams
|
||||
}
|
||||
|
||||
func Test_DiscoverMedias(t *testing.T) {
|
||||
providers := testx.Default().With(
|
||||
Provide,
|
||||
)
|
||||
|
||||
testx.Serve(providers, t, func(params ServiceInjectParams) {
|
||||
suite.Run(t, &ServiceTestSuite{ServiceInjectParams: params})
|
||||
})
|
||||
}
|
||||
|
||||
func (s *ServiceTestSuite) Test_Service() {
|
||||
Convey("Test Service", s.T(), func() {
|
||||
So(s.Svc, ShouldNotBeNil)
|
||||
})
|
||||
}
|
||||
30
backend/app/http/tenants/controller.go
Normal file
30
backend/app/http/tenants/controller.go
Normal file
@@ -0,0 +1,30 @@
|
||||
package tenants
|
||||
|
||||
import (
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// @provider
|
||||
type Controller struct {
|
||||
svc *Service
|
||||
log *log.Entry `inject:"false"`
|
||||
}
|
||||
|
||||
func (c *Controller) Prepare() error {
|
||||
c.log = log.WithField("module", "tenants.Controller")
|
||||
return nil
|
||||
}
|
||||
|
||||
// Test godoc
|
||||
//
|
||||
// @Summary Test
|
||||
// @Description Test
|
||||
// @Tags Test
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param id path int true "AccountID"
|
||||
// @Param queryFilter query dto.AlarmListQuery true "AlarmListQueryFilter"
|
||||
// @Param pageFilter query common.PageQueryFilter true "PageQueryFilter"
|
||||
// @Param sortFilter query common.SortQueryFilter true "SortQueryFilter"
|
||||
// @Success 200 {object} common.PageDataResponse{list=dto.AlarmItem}
|
||||
// @Router /v1/test/:id<int> [get]
|
||||
1
backend/app/http/tenants/dto.go
Normal file
1
backend/app/http/tenants/dto.go
Normal file
@@ -0,0 +1 @@
|
||||
package tenants
|
||||
40
backend/app/http/tenants/provider.gen.go
Executable file
40
backend/app/http/tenants/provider.gen.go
Executable file
@@ -0,0 +1,40 @@
|
||||
package tenants
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
|
||||
"git.ipao.vip/rogeecn/atom/container"
|
||||
"git.ipao.vip/rogeecn/atom/utils/opt"
|
||||
)
|
||||
|
||||
func Provide(opts ...opt.Option) error {
|
||||
if err := container.Container.Provide(func(
|
||||
svc *Service,
|
||||
) (*Controller, error) {
|
||||
obj := &Controller{
|
||||
svc: svc,
|
||||
}
|
||||
if err := obj.Prepare(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return obj, nil
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := container.Container.Provide(func(
|
||||
db *sql.DB,
|
||||
) (*Service, error) {
|
||||
obj := &Service{
|
||||
db: db,
|
||||
}
|
||||
if err := obj.Prepare(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return obj, nil
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
20
backend/app/http/tenants/service.go
Normal file
20
backend/app/http/tenants/service.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package tenants
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
|
||||
. "github.com/go-jet/jet/v2/postgres"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// @provider:except
|
||||
type Service struct {
|
||||
db *sql.DB
|
||||
log *log.Entry `inject:"false"`
|
||||
}
|
||||
|
||||
func (svc *Service) Prepare() error {
|
||||
svc.log = log.WithField("module", "tenants.service")
|
||||
_ = Int(1)
|
||||
return nil
|
||||
}
|
||||
37
backend/app/http/tenants/service_test.go
Normal file
37
backend/app/http/tenants/service_test.go
Normal file
@@ -0,0 +1,37 @@
|
||||
package tenants
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"backend/app/service/testx"
|
||||
|
||||
. "github.com/smartystreets/goconvey/convey"
|
||||
"github.com/stretchr/testify/suite"
|
||||
"go.uber.org/dig"
|
||||
)
|
||||
|
||||
type ServiceInjectParams struct {
|
||||
dig.In
|
||||
Svc *Service
|
||||
}
|
||||
|
||||
type ServiceTestSuite struct {
|
||||
suite.Suite
|
||||
ServiceInjectParams
|
||||
}
|
||||
|
||||
func Test_DiscoverMedias(t *testing.T) {
|
||||
providers := testx.Default().With(
|
||||
Provide,
|
||||
)
|
||||
|
||||
testx.Serve(providers, t, func(params ServiceInjectParams) {
|
||||
suite.Run(t, &ServiceTestSuite{ServiceInjectParams: params})
|
||||
})
|
||||
}
|
||||
|
||||
func (s *ServiceTestSuite) Test_Service() {
|
||||
Convey("Test Service", s.T(), func() {
|
||||
So(s.Svc, ShouldNotBeNil)
|
||||
})
|
||||
}
|
||||
30
backend/app/http/users/controller.go
Normal file
30
backend/app/http/users/controller.go
Normal file
@@ -0,0 +1,30 @@
|
||||
package users
|
||||
|
||||
import (
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// @provider
|
||||
type Controller struct {
|
||||
svc *Service
|
||||
log *log.Entry `inject:"false"`
|
||||
}
|
||||
|
||||
func (c *Controller) Prepare() error {
|
||||
c.log = log.WithField("module", "users.Controller")
|
||||
return nil
|
||||
}
|
||||
|
||||
// Test godoc
|
||||
//
|
||||
// @Summary Test
|
||||
// @Description Test
|
||||
// @Tags Test
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param id path int true "AccountID"
|
||||
// @Param queryFilter query dto.AlarmListQuery true "AlarmListQueryFilter"
|
||||
// @Param pageFilter query common.PageQueryFilter true "PageQueryFilter"
|
||||
// @Param sortFilter query common.SortQueryFilter true "SortQueryFilter"
|
||||
// @Success 200 {object} common.PageDataResponse{list=dto.AlarmItem}
|
||||
// @Router /v1/test/:id<int> [get]
|
||||
1
backend/app/http/users/dto.go
Normal file
1
backend/app/http/users/dto.go
Normal file
@@ -0,0 +1 @@
|
||||
package users
|
||||
40
backend/app/http/users/provider.gen.go
Executable file
40
backend/app/http/users/provider.gen.go
Executable file
@@ -0,0 +1,40 @@
|
||||
package users
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
|
||||
"git.ipao.vip/rogeecn/atom/container"
|
||||
"git.ipao.vip/rogeecn/atom/utils/opt"
|
||||
)
|
||||
|
||||
func Provide(opts ...opt.Option) error {
|
||||
if err := container.Container.Provide(func(
|
||||
svc *Service,
|
||||
) (*Controller, error) {
|
||||
obj := &Controller{
|
||||
svc: svc,
|
||||
}
|
||||
if err := obj.Prepare(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return obj, nil
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := container.Container.Provide(func(
|
||||
db *sql.DB,
|
||||
) (*Service, error) {
|
||||
obj := &Service{
|
||||
db: db,
|
||||
}
|
||||
if err := obj.Prepare(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return obj, nil
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
201
backend/app/http/users/service.go
Normal file
201
backend/app/http/users/service.go
Normal file
@@ -0,0 +1,201 @@
|
||||
package users
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"time"
|
||||
|
||||
"backend/app/events"
|
||||
"backend/database/fields"
|
||||
"backend/database/models/qvyun_v2/public/model"
|
||||
"backend/database/models/qvyun_v2/public/table"
|
||||
"backend/pkg/oauth"
|
||||
"backend/providers/event"
|
||||
"backend/providers/otel"
|
||||
|
||||
. "github.com/go-jet/jet/v2/postgres"
|
||||
"github.com/samber/lo"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.15.0"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
// @provider:except
|
||||
type Service struct {
|
||||
db *sql.DB
|
||||
event *event.PubSub
|
||||
log *log.Entry `inject:"false"`
|
||||
}
|
||||
|
||||
func (svc *Service) Prepare() error {
|
||||
svc.log = log.WithField("module", "users.service")
|
||||
_ = Int(1)
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetUsersByOpenID Get user by open id
|
||||
func (svc *Service) GetUserByOpenID(ctx context.Context, channel fields.AuthChannel, openID string) (*model.Users, error) {
|
||||
_, span := otel.Start(ctx, "users.service.GetUsersByOpenID")
|
||||
defer span.End()
|
||||
|
||||
userId, err := svc.GetUserIDByOpenID(ctx, channel, openID)
|
||||
if err != nil {
|
||||
// span 添加用户不存在事件
|
||||
span.AddEvent("user not found")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tbl := table.Users
|
||||
stmt := tbl.SELECT(tbl.AllColumns).WHERE(tbl.ID.EQ(Int64(userId)))
|
||||
span.SetAttributes(semconv.DBStatementKey.String(stmt.DebugSql()))
|
||||
|
||||
var user model.Users
|
||||
if err := stmt.QueryContext(ctx, svc.db, &user); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &user, nil
|
||||
}
|
||||
|
||||
func (svc *Service) GetUserIDByOpenID(ctx context.Context, channel fields.AuthChannel, openID string) (int64, error) {
|
||||
_, span := otel.Start(ctx, "users.service.GetUserIDByOpenID")
|
||||
defer span.End()
|
||||
|
||||
span.SetAttributes(
|
||||
attribute.String("channel", channel.String()),
|
||||
attribute.String("openID", openID),
|
||||
)
|
||||
|
||||
tbl := table.UserOauths
|
||||
|
||||
stmt := tbl.
|
||||
SELECT(tbl.UserID.AS("user_id")).
|
||||
WHERE(
|
||||
tbl.Channel.EQ(Int16(int16(channel))).
|
||||
AND(tbl.OpenID.EQ(String(openID))),
|
||||
)
|
||||
span.SetAttributes(semconv.DBStatementKey.String(stmt.DebugSql()))
|
||||
|
||||
var result struct {
|
||||
UserID int64
|
||||
}
|
||||
|
||||
if err := stmt.QueryContext(ctx, svc.db, &result); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return result.UserID, nil
|
||||
}
|
||||
|
||||
// CreateUser
|
||||
func (svc *Service) CreateUser(ctx context.Context, user *model.Users) (*model.Users, error) {
|
||||
_, span := otel.Start(ctx, "users.service.CreateUser")
|
||||
defer span.End()
|
||||
span.SetAttributes(
|
||||
attribute.String("user.username", user.Username),
|
||||
attribute.String("user.email", user.Email),
|
||||
attribute.String("user.phone", user.Phone),
|
||||
)
|
||||
|
||||
if user.CreatedAt.IsZero() {
|
||||
user.CreatedAt = time.Now()
|
||||
}
|
||||
|
||||
if user.UpdatedAt.IsZero() {
|
||||
user.UpdatedAt = time.Now()
|
||||
}
|
||||
|
||||
user.Status = int16(fields.UserStatusPending)
|
||||
|
||||
// use bcrypt to hash password
|
||||
pwd, err := bcrypt.GenerateFromPassword([]byte(user.Password), bcrypt.DefaultCost)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
user.Password = string(pwd)
|
||||
|
||||
tbl := table.Users
|
||||
stmt := tbl.INSERT(tbl.MutableColumns).MODEL(user).ON_CONFLICT(tbl.Email, tbl.Phone, tbl.Username).DO_NOTHING().RETURNING(tbl.AllColumns)
|
||||
span.SetAttributes(semconv.DBStatementKey.String(stmt.DebugSql()))
|
||||
|
||||
var m model.Users
|
||||
if err = stmt.QueryContext(ctx, svc.db, &m); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// if user created successfully, trigger event
|
||||
span.AddEvent("user created")
|
||||
svc.event.Publish(&events.UserRegister{ID: m.ID})
|
||||
|
||||
return &m, err
|
||||
}
|
||||
|
||||
// GetUserByID
|
||||
func (svc *Service) GetUserByID(ctx context.Context, userID int64) (*model.Users, error) {
|
||||
_, span := otel.Start(ctx, "users.service.GetUserByID")
|
||||
defer span.End()
|
||||
span.SetAttributes(
|
||||
attribute.Int64("user.id", userID),
|
||||
)
|
||||
|
||||
tbl := table.Users
|
||||
stmt := tbl.SELECT(tbl.AllColumns).WHERE(tbl.ID.EQ(Int64(userID)))
|
||||
span.SetAttributes(semconv.DBStatementKey.String(stmt.DebugSql()))
|
||||
|
||||
var user model.Users
|
||||
if err := stmt.QueryContext(ctx, svc.db, &user); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &user, nil
|
||||
}
|
||||
|
||||
// AttachUserOAuth
|
||||
func (svc *Service) AttachUserOAuth(ctx context.Context, user *model.Users, channel fields.AuthChannel, oauthInfo oauth.OAuthInfo) error {
|
||||
_, span := otel.Start(ctx, "users.service.AttachUserOAuth")
|
||||
defer span.End()
|
||||
span.SetAttributes(
|
||||
attribute.Int64("user.id", user.ID),
|
||||
attribute.String("channel", channel.String()),
|
||||
attribute.String("openID", oauthInfo.GetOpenID()),
|
||||
)
|
||||
|
||||
m := &model.UserOauths{
|
||||
ID: 0,
|
||||
CreatedAt: time.Now(),
|
||||
UpdatedAt: time.Now(),
|
||||
DeletedAt: nil,
|
||||
Channel: channel,
|
||||
UserID: user.ID,
|
||||
UnionID: lo.ToPtr(oauthInfo.GetUnionID()),
|
||||
OpenID: oauthInfo.GetOpenID(),
|
||||
AccessToken: oauthInfo.GetAccessToken(),
|
||||
RefreshToken: oauthInfo.GetRefreshToken(),
|
||||
ExpireAt: oauthInfo.GetExpiredAt(),
|
||||
Meta: new(string),
|
||||
}
|
||||
|
||||
tbl := table.UserOauths
|
||||
stmt := tbl.
|
||||
INSERT(tbl.MutableColumns).
|
||||
MODEL(m).
|
||||
ON_CONFLICT(tbl.Channel, tbl.UserID).
|
||||
DO_UPDATE(
|
||||
SET(
|
||||
tbl.UnionID.SET(String(oauthInfo.GetUnionID())),
|
||||
tbl.OpenID.SET(String(oauthInfo.GetOpenID())),
|
||||
tbl.AccessToken.SET(String(oauthInfo.GetAccessToken())),
|
||||
tbl.RefreshToken.SET(String(oauthInfo.GetRefreshToken())),
|
||||
tbl.ExpireAt.SET(TimestampT(oauthInfo.GetExpiredAt())),
|
||||
),
|
||||
)
|
||||
|
||||
span.SetAttributes(semconv.DBStatementKey.String(stmt.DebugSql()))
|
||||
|
||||
if _, err := stmt.ExecContext(ctx, svc.db); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
109
backend/app/http/users/service_test.go
Normal file
109
backend/app/http/users/service_test.go
Normal file
@@ -0,0 +1,109 @@
|
||||
package users
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"backend/app/service/testx"
|
||||
"backend/database"
|
||||
"backend/database/fields"
|
||||
"backend/database/models/qvyun_v2/public/model"
|
||||
"backend/database/models/qvyun_v2/public/table"
|
||||
|
||||
"git.ipao.vip/rogeecn/atom/contracts"
|
||||
. "github.com/smartystreets/goconvey/convey"
|
||||
"github.com/stretchr/testify/suite"
|
||||
"go.uber.org/dig"
|
||||
)
|
||||
|
||||
type ServiceInjectParams struct {
|
||||
dig.In
|
||||
Initials []contracts.Initial `group:"initials"`
|
||||
|
||||
Svc *Service
|
||||
}
|
||||
|
||||
type ServiceTestSuite struct {
|
||||
suite.Suite
|
||||
ServiceInjectParams
|
||||
}
|
||||
|
||||
func Test_DiscoverMedias(t *testing.T) {
|
||||
providers := testx.Default().With(
|
||||
Provide,
|
||||
)
|
||||
|
||||
testx.Serve(providers, t, func(params ServiceInjectParams) {
|
||||
suite.Run(t, &ServiceTestSuite{ServiceInjectParams: params})
|
||||
})
|
||||
}
|
||||
|
||||
func (s *ServiceTestSuite) Test_GetUserIDByOpenID() {
|
||||
Convey("Test GetUserIDByOpenID", s.T(), func() {
|
||||
v, err := fields.AuthChannelWeChat.Value()
|
||||
So(err, ShouldBeNil)
|
||||
So(v, ShouldEqual, 1)
|
||||
|
||||
Convey("truncate tables", func() {
|
||||
err := database.Truncate(context.Background(), s.Svc.db, table.UserOauths.TableName())
|
||||
So(err, ShouldBeNil)
|
||||
})
|
||||
|
||||
Convey("insert data", func() {
|
||||
m := model.UserOauths{
|
||||
Channel: fields.AuthChannelWeChat,
|
||||
UserID: 1,
|
||||
OpenID: "test_open_id",
|
||||
AccessKey: "test_access_key",
|
||||
AccessToken: "test_access_token",
|
||||
RefreshToken: "test_refresh_token",
|
||||
ExpireAt: time.Now().Add(time.Hour),
|
||||
}
|
||||
|
||||
tbl := table.UserOauths
|
||||
stmt := tbl.INSERT(
|
||||
tbl.MutableColumns.Except(
|
||||
tbl.CreatedAt,
|
||||
tbl.UpdatedAt,
|
||||
tbl.DeletedAt,
|
||||
tbl.UnionID,
|
||||
tbl.Meta,
|
||||
),
|
||||
).MODEL(m)
|
||||
s.T().Log(stmt.Sql())
|
||||
|
||||
_, err := stmt.ExecContext(context.Background(), s.Svc.db)
|
||||
So(err, ShouldBeNil)
|
||||
})
|
||||
|
||||
Convey("get user id by open id", func() {
|
||||
userID, err := s.Svc.GetUserIDByOpenID(context.Background(), fields.AuthChannelWeChat, "test_open_id")
|
||||
So(err, ShouldBeNil)
|
||||
So(userID, ShouldEqual, 1)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// CreateUser
|
||||
func (s *ServiceTestSuite) Test_CreateUser() {
|
||||
Convey("Test CreateUser", s.T(), func() {
|
||||
Convey("truncate tables", func() {
|
||||
err := database.Truncate(context.Background(), s.Svc.db, table.Users.TableName())
|
||||
So(err, ShouldBeNil)
|
||||
})
|
||||
|
||||
Convey("create user", func() {
|
||||
user := &model.Users{
|
||||
Email: "test@qq.com",
|
||||
Phone: "12345678901",
|
||||
Username: "test",
|
||||
Password: "test",
|
||||
Age: 18,
|
||||
}
|
||||
|
||||
_, err := s.Svc.CreateUser(context.Background(), user)
|
||||
So(err, ShouldBeNil)
|
||||
})
|
||||
})
|
||||
}
|
||||
@@ -1,13 +1,28 @@
|
||||
package middlewares
|
||||
|
||||
import (
|
||||
"backend/providers/app"
|
||||
"backend/providers/jwt"
|
||||
"backend/providers/storage"
|
||||
"backend/providers/wechat"
|
||||
|
||||
"git.ipao.vip/rogeecn/atom/container"
|
||||
"git.ipao.vip/rogeecn/atom/utils/opt"
|
||||
)
|
||||
|
||||
func Provide(opts ...opt.Option) error {
|
||||
if err := container.Container.Provide(func() (*Middlewares, error) {
|
||||
obj := &Middlewares{}
|
||||
if err := container.Container.Provide(func(
|
||||
app *app.Config,
|
||||
client *wechat.Client,
|
||||
jwt *jwt.JWT,
|
||||
storagePath *storage.Config,
|
||||
) (*Middlewares, error) {
|
||||
obj := &Middlewares{
|
||||
app: app,
|
||||
client: client,
|
||||
jwt: jwt,
|
||||
storagePath: storagePath,
|
||||
}
|
||||
if err := obj.Prepare(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
"backend/app/events"
|
||||
"backend/app/service"
|
||||
"backend/providers/app"
|
||||
providerEvents "backend/providers/events"
|
||||
"backend/providers/event"
|
||||
"backend/providers/postgres"
|
||||
|
||||
"git.ipao.vip/rogeecn/atom"
|
||||
@@ -41,7 +41,7 @@ type Service struct {
|
||||
dig.In
|
||||
|
||||
App *app.Config
|
||||
PubSub *providerEvents.PubSub
|
||||
PubSub *event.PubSub
|
||||
Initials []contracts.Initial `group:"initials"`
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ import (
|
||||
"git.ipao.vip/rogeecn/atom"
|
||||
"git.ipao.vip/rogeecn/atom/container"
|
||||
"git.ipao.vip/rogeecn/atom/contracts"
|
||||
"github.com/gofiber/fiber/v3"
|
||||
"github.com/gofiber/fiber/v3/middleware/favicon"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
@@ -69,6 +70,10 @@ func Serve(cmd *cobra.Command, args []string) error {
|
||||
svc.Http.Engine.Get("/swagger/*", swagger.HandlerDefault)
|
||||
}
|
||||
|
||||
svc.Http.Engine.Get("MP_verify_dEF9kn8rJlBsuLKk.txt", func(c fiber.Ctx) error {
|
||||
return c.SendString("dEF9kn8rJlBsuLKk")
|
||||
})
|
||||
|
||||
svc.Http.Engine.Use(svc.Middlewares.WeChatVerify)
|
||||
svc.Http.Engine.Use(errorx.Middleware)
|
||||
svc.Http.Engine.Use(favicon.New(favicon.Config{
|
||||
|
||||
@@ -2,7 +2,7 @@ package service
|
||||
|
||||
import (
|
||||
"backend/providers/app"
|
||||
"backend/providers/events"
|
||||
"backend/providers/event"
|
||||
|
||||
"git.ipao.vip/rogeecn/atom/container"
|
||||
)
|
||||
@@ -10,6 +10,6 @@ import (
|
||||
func Default(providers ...container.ProviderContainer) container.Providers {
|
||||
return append(container.Providers{
|
||||
app.DefaultProvider(),
|
||||
events.DefaultProvider(),
|
||||
event.DefaultProvider(),
|
||||
}, providers...)
|
||||
}
|
||||
|
||||
@@ -4,18 +4,27 @@ import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"backend/providers/otel"
|
||||
"backend/providers/postgres"
|
||||
|
||||
"git.ipao.vip/rogeecn/atom"
|
||||
"git.ipao.vip/rogeecn/atom/container"
|
||||
"github.com/rogeecn/fabfile"
|
||||
log "github.com/sirupsen/logrus"
|
||||
. "github.com/smartystreets/goconvey/convey"
|
||||
)
|
||||
|
||||
func Default(providers ...container.ProviderContainer) container.Providers {
|
||||
return append(container.Providers{}, providers...)
|
||||
return append(container.Providers{
|
||||
otel.DefaultProvider(),
|
||||
postgres.DefaultProvider(),
|
||||
}, providers...)
|
||||
}
|
||||
|
||||
func Serve(providers container.Providers, t *testing.T, invoke any) {
|
||||
Convey("tests boot up", t, func() {
|
||||
log.SetLevel(log.DebugLevel)
|
||||
|
||||
file := fabfile.MustFind("config.toml")
|
||||
|
||||
localEnv := os.Getenv("ENV_LOCAL")
|
||||
|
||||
277
backend/database/fields/user_oauths.gen.go
Normal file
277
backend/database/fields/user_oauths.gen.go
Normal file
@@ -0,0 +1,277 @@
|
||||
// Code generated by go-enum DO NOT EDIT.
|
||||
// Version: -
|
||||
// Revision: -
|
||||
// Build Date: -
|
||||
// Built By: -
|
||||
|
||||
package fields
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
// AuthChannelWeChat is a AuthChannel of type WeChat.
|
||||
AuthChannelWeChat AuthChannel = iota + 1
|
||||
// AuthChannelWeiBo is a AuthChannel of type WeiBo.
|
||||
AuthChannelWeiBo
|
||||
// AuthChannelQQ is a AuthChannel of type QQ.
|
||||
AuthChannelQQ
|
||||
// AuthChannelGithub is a AuthChannel of type Github.
|
||||
AuthChannelGithub
|
||||
// AuthChannelGoogle is a AuthChannel of type Google.
|
||||
AuthChannelGoogle
|
||||
// AuthChannelFacebook is a AuthChannel of type Facebook.
|
||||
AuthChannelFacebook
|
||||
// AuthChannelTwitter is a AuthChannel of type Twitter.
|
||||
AuthChannelTwitter
|
||||
// AuthChannelLinkedIn is a AuthChannel of type LinkedIn.
|
||||
AuthChannelLinkedIn
|
||||
// AuthChannelApple is a AuthChannel of type Apple.
|
||||
AuthChannelApple
|
||||
)
|
||||
|
||||
var ErrInvalidAuthChannel = fmt.Errorf("not a valid AuthChannel, try [%s]", strings.Join(_AuthChannelNames, ", "))
|
||||
|
||||
const _AuthChannelName = "WeChatWeiBoQQGithubGoogleFacebookTwitterLinkedInApple"
|
||||
|
||||
var _AuthChannelNames = []string{
|
||||
_AuthChannelName[0:6],
|
||||
_AuthChannelName[6:11],
|
||||
_AuthChannelName[11:13],
|
||||
_AuthChannelName[13:19],
|
||||
_AuthChannelName[19:25],
|
||||
_AuthChannelName[25:33],
|
||||
_AuthChannelName[33:40],
|
||||
_AuthChannelName[40:48],
|
||||
_AuthChannelName[48:53],
|
||||
}
|
||||
|
||||
// AuthChannelNames returns a list of possible string values of AuthChannel.
|
||||
func AuthChannelNames() []string {
|
||||
tmp := make([]string, len(_AuthChannelNames))
|
||||
copy(tmp, _AuthChannelNames)
|
||||
return tmp
|
||||
}
|
||||
|
||||
// AuthChannelValues returns a list of the values for AuthChannel
|
||||
func AuthChannelValues() []AuthChannel {
|
||||
return []AuthChannel{
|
||||
AuthChannelWeChat,
|
||||
AuthChannelWeiBo,
|
||||
AuthChannelQQ,
|
||||
AuthChannelGithub,
|
||||
AuthChannelGoogle,
|
||||
AuthChannelFacebook,
|
||||
AuthChannelTwitter,
|
||||
AuthChannelLinkedIn,
|
||||
AuthChannelApple,
|
||||
}
|
||||
}
|
||||
|
||||
var _AuthChannelMap = map[AuthChannel]string{
|
||||
AuthChannelWeChat: _AuthChannelName[0:6],
|
||||
AuthChannelWeiBo: _AuthChannelName[6:11],
|
||||
AuthChannelQQ: _AuthChannelName[11:13],
|
||||
AuthChannelGithub: _AuthChannelName[13:19],
|
||||
AuthChannelGoogle: _AuthChannelName[19:25],
|
||||
AuthChannelFacebook: _AuthChannelName[25:33],
|
||||
AuthChannelTwitter: _AuthChannelName[33:40],
|
||||
AuthChannelLinkedIn: _AuthChannelName[40:48],
|
||||
AuthChannelApple: _AuthChannelName[48:53],
|
||||
}
|
||||
|
||||
// String implements the Stringer interface.
|
||||
func (x AuthChannel) String() string {
|
||||
if str, ok := _AuthChannelMap[x]; ok {
|
||||
return str
|
||||
}
|
||||
return fmt.Sprintf("AuthChannel(%d)", x)
|
||||
}
|
||||
|
||||
// IsValid provides a quick way to determine if the typed value is
|
||||
// part of the allowed enumerated values
|
||||
func (x AuthChannel) IsValid() bool {
|
||||
_, ok := _AuthChannelMap[x]
|
||||
return ok
|
||||
}
|
||||
|
||||
var _AuthChannelValue = map[string]AuthChannel{
|
||||
_AuthChannelName[0:6]: AuthChannelWeChat,
|
||||
_AuthChannelName[6:11]: AuthChannelWeiBo,
|
||||
_AuthChannelName[11:13]: AuthChannelQQ,
|
||||
_AuthChannelName[13:19]: AuthChannelGithub,
|
||||
_AuthChannelName[19:25]: AuthChannelGoogle,
|
||||
_AuthChannelName[25:33]: AuthChannelFacebook,
|
||||
_AuthChannelName[33:40]: AuthChannelTwitter,
|
||||
_AuthChannelName[40:48]: AuthChannelLinkedIn,
|
||||
_AuthChannelName[48:53]: AuthChannelApple,
|
||||
}
|
||||
|
||||
// ParseAuthChannel attempts to convert a string to a AuthChannel.
|
||||
func ParseAuthChannel(name string) (AuthChannel, error) {
|
||||
if x, ok := _AuthChannelValue[name]; ok {
|
||||
return x, nil
|
||||
}
|
||||
return AuthChannel(0), fmt.Errorf("%s is %w", name, ErrInvalidAuthChannel)
|
||||
}
|
||||
|
||||
var errAuthChannelNilPtr = errors.New("value pointer is nil") // one per type for package clashes
|
||||
|
||||
// Scan implements the Scanner interface.
|
||||
func (x *AuthChannel) Scan(value interface{}) (err error) {
|
||||
if value == nil {
|
||||
*x = AuthChannel(0)
|
||||
return
|
||||
}
|
||||
|
||||
// A wider range of scannable types.
|
||||
// driver.Value values at the top of the list for expediency
|
||||
switch v := value.(type) {
|
||||
case int64:
|
||||
*x = AuthChannel(v)
|
||||
case string:
|
||||
*x, err = ParseAuthChannel(v)
|
||||
if err != nil {
|
||||
// try parsing the integer value as a string
|
||||
if val, verr := strconv.Atoi(v); verr == nil {
|
||||
*x, err = AuthChannel(val), nil
|
||||
}
|
||||
}
|
||||
case []byte:
|
||||
*x, err = ParseAuthChannel(string(v))
|
||||
if err != nil {
|
||||
// try parsing the integer value as a string
|
||||
if val, verr := strconv.Atoi(string(v)); verr == nil {
|
||||
*x, err = AuthChannel(val), nil
|
||||
}
|
||||
}
|
||||
case AuthChannel:
|
||||
*x = v
|
||||
case int:
|
||||
*x = AuthChannel(v)
|
||||
case *AuthChannel:
|
||||
if v == nil {
|
||||
return errAuthChannelNilPtr
|
||||
}
|
||||
*x = *v
|
||||
case uint:
|
||||
*x = AuthChannel(v)
|
||||
case uint64:
|
||||
*x = AuthChannel(v)
|
||||
case *int:
|
||||
if v == nil {
|
||||
return errAuthChannelNilPtr
|
||||
}
|
||||
*x = AuthChannel(*v)
|
||||
case *int64:
|
||||
if v == nil {
|
||||
return errAuthChannelNilPtr
|
||||
}
|
||||
*x = AuthChannel(*v)
|
||||
case float64: // json marshals everything as a float64 if it's a number
|
||||
*x = AuthChannel(v)
|
||||
case *float64: // json marshals everything as a float64 if it's a number
|
||||
if v == nil {
|
||||
return errAuthChannelNilPtr
|
||||
}
|
||||
*x = AuthChannel(*v)
|
||||
case *uint:
|
||||
if v == nil {
|
||||
return errAuthChannelNilPtr
|
||||
}
|
||||
*x = AuthChannel(*v)
|
||||
case *uint64:
|
||||
if v == nil {
|
||||
return errAuthChannelNilPtr
|
||||
}
|
||||
*x = AuthChannel(*v)
|
||||
case *string:
|
||||
if v == nil {
|
||||
return errAuthChannelNilPtr
|
||||
}
|
||||
*x, err = ParseAuthChannel(*v)
|
||||
if err != nil {
|
||||
// try parsing the integer value as a string
|
||||
if val, verr := strconv.Atoi(*v); verr == nil {
|
||||
*x, err = AuthChannel(val), nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Value implements the driver Valuer interface.
|
||||
func (x AuthChannel) Value() (driver.Value, error) {
|
||||
return int64(x), nil
|
||||
}
|
||||
|
||||
// Set implements the Golang flag.Value interface func.
|
||||
func (x *AuthChannel) Set(val string) error {
|
||||
v, err := ParseAuthChannel(val)
|
||||
*x = v
|
||||
return err
|
||||
}
|
||||
|
||||
// Get implements the Golang flag.Getter interface func.
|
||||
func (x *AuthChannel) Get() interface{} {
|
||||
return *x
|
||||
}
|
||||
|
||||
// Type implements the github.com/spf13/pFlag Value interface.
|
||||
func (x *AuthChannel) Type() string {
|
||||
return "AuthChannel"
|
||||
}
|
||||
|
||||
type NullAuthChannel struct {
|
||||
AuthChannel AuthChannel
|
||||
Valid bool
|
||||
}
|
||||
|
||||
func NewNullAuthChannel(val interface{}) (x NullAuthChannel) {
|
||||
x.Scan(val) // yes, we ignore this error, it will just be an invalid value.
|
||||
return
|
||||
}
|
||||
|
||||
// Scan implements the Scanner interface.
|
||||
func (x *NullAuthChannel) Scan(value interface{}) (err error) {
|
||||
if value == nil {
|
||||
x.AuthChannel, x.Valid = AuthChannel(0), false
|
||||
return
|
||||
}
|
||||
|
||||
err = x.AuthChannel.Scan(value)
|
||||
x.Valid = (err == nil)
|
||||
return
|
||||
}
|
||||
|
||||
// Value implements the driver Valuer interface.
|
||||
func (x NullAuthChannel) Value() (driver.Value, error) {
|
||||
if !x.Valid {
|
||||
return nil, nil
|
||||
}
|
||||
// driver.Value accepts int64 for int values.
|
||||
return int64(x.AuthChannel), nil
|
||||
}
|
||||
|
||||
type NullAuthChannelStr struct {
|
||||
NullAuthChannel
|
||||
}
|
||||
|
||||
func NewNullAuthChannelStr(val interface{}) (x NullAuthChannelStr) {
|
||||
x.Scan(val) // yes, we ignore this error, it will just be an invalid value.
|
||||
return
|
||||
}
|
||||
|
||||
// Value implements the driver Valuer interface.
|
||||
func (x NullAuthChannelStr) Value() (driver.Value, error) {
|
||||
if !x.Valid {
|
||||
return nil, nil
|
||||
}
|
||||
return x.AuthChannel.String(), nil
|
||||
}
|
||||
5
backend/database/fields/user_oauths.go
Normal file
5
backend/database/fields/user_oauths.go
Normal file
@@ -0,0 +1,5 @@
|
||||
package fields
|
||||
|
||||
// swagger:enum AuthChannel
|
||||
// ENUM( WeChat = 1 , WeiBo, QQ, Github, Google, Facebook, Twitter, LinkedIn, Apple)
|
||||
type AuthChannel int16
|
||||
241
backend/database/fields/users.gen.go
Normal file
241
backend/database/fields/users.gen.go
Normal file
@@ -0,0 +1,241 @@
|
||||
// Code generated by go-enum DO NOT EDIT.
|
||||
// Version: -
|
||||
// Revision: -
|
||||
// Build Date: -
|
||||
// Built By: -
|
||||
|
||||
package fields
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
// UserStatusPending is a UserStatus of type Pending.
|
||||
UserStatusPending UserStatus = iota
|
||||
// UserStatusVerified is a UserStatus of type Verified.
|
||||
UserStatusVerified
|
||||
// UserStatusBlocked is a UserStatus of type Blocked.
|
||||
UserStatusBlocked
|
||||
)
|
||||
|
||||
var ErrInvalidUserStatus = fmt.Errorf("not a valid UserStatus, try [%s]", strings.Join(_UserStatusNames, ", "))
|
||||
|
||||
const _UserStatusName = "PendingVerifiedBlocked"
|
||||
|
||||
var _UserStatusNames = []string{
|
||||
_UserStatusName[0:7],
|
||||
_UserStatusName[7:15],
|
||||
_UserStatusName[15:22],
|
||||
}
|
||||
|
||||
// UserStatusNames returns a list of possible string values of UserStatus.
|
||||
func UserStatusNames() []string {
|
||||
tmp := make([]string, len(_UserStatusNames))
|
||||
copy(tmp, _UserStatusNames)
|
||||
return tmp
|
||||
}
|
||||
|
||||
// UserStatusValues returns a list of the values for UserStatus
|
||||
func UserStatusValues() []UserStatus {
|
||||
return []UserStatus{
|
||||
UserStatusPending,
|
||||
UserStatusVerified,
|
||||
UserStatusBlocked,
|
||||
}
|
||||
}
|
||||
|
||||
var _UserStatusMap = map[UserStatus]string{
|
||||
UserStatusPending: _UserStatusName[0:7],
|
||||
UserStatusVerified: _UserStatusName[7:15],
|
||||
UserStatusBlocked: _UserStatusName[15:22],
|
||||
}
|
||||
|
||||
// String implements the Stringer interface.
|
||||
func (x UserStatus) String() string {
|
||||
if str, ok := _UserStatusMap[x]; ok {
|
||||
return str
|
||||
}
|
||||
return fmt.Sprintf("UserStatus(%d)", x)
|
||||
}
|
||||
|
||||
// IsValid provides a quick way to determine if the typed value is
|
||||
// part of the allowed enumerated values
|
||||
func (x UserStatus) IsValid() bool {
|
||||
_, ok := _UserStatusMap[x]
|
||||
return ok
|
||||
}
|
||||
|
||||
var _UserStatusValue = map[string]UserStatus{
|
||||
_UserStatusName[0:7]: UserStatusPending,
|
||||
_UserStatusName[7:15]: UserStatusVerified,
|
||||
_UserStatusName[15:22]: UserStatusBlocked,
|
||||
}
|
||||
|
||||
// ParseUserStatus attempts to convert a string to a UserStatus.
|
||||
func ParseUserStatus(name string) (UserStatus, error) {
|
||||
if x, ok := _UserStatusValue[name]; ok {
|
||||
return x, nil
|
||||
}
|
||||
return UserStatus(0), fmt.Errorf("%s is %w", name, ErrInvalidUserStatus)
|
||||
}
|
||||
|
||||
var errUserStatusNilPtr = errors.New("value pointer is nil") // one per type for package clashes
|
||||
|
||||
// Scan implements the Scanner interface.
|
||||
func (x *UserStatus) Scan(value interface{}) (err error) {
|
||||
if value == nil {
|
||||
*x = UserStatus(0)
|
||||
return
|
||||
}
|
||||
|
||||
// A wider range of scannable types.
|
||||
// driver.Value values at the top of the list for expediency
|
||||
switch v := value.(type) {
|
||||
case int64:
|
||||
*x = UserStatus(v)
|
||||
case string:
|
||||
*x, err = ParseUserStatus(v)
|
||||
if err != nil {
|
||||
// try parsing the integer value as a string
|
||||
if val, verr := strconv.Atoi(v); verr == nil {
|
||||
*x, err = UserStatus(val), nil
|
||||
}
|
||||
}
|
||||
case []byte:
|
||||
*x, err = ParseUserStatus(string(v))
|
||||
if err != nil {
|
||||
// try parsing the integer value as a string
|
||||
if val, verr := strconv.Atoi(string(v)); verr == nil {
|
||||
*x, err = UserStatus(val), nil
|
||||
}
|
||||
}
|
||||
case UserStatus:
|
||||
*x = v
|
||||
case int:
|
||||
*x = UserStatus(v)
|
||||
case *UserStatus:
|
||||
if v == nil {
|
||||
return errUserStatusNilPtr
|
||||
}
|
||||
*x = *v
|
||||
case uint:
|
||||
*x = UserStatus(v)
|
||||
case uint64:
|
||||
*x = UserStatus(v)
|
||||
case *int:
|
||||
if v == nil {
|
||||
return errUserStatusNilPtr
|
||||
}
|
||||
*x = UserStatus(*v)
|
||||
case *int64:
|
||||
if v == nil {
|
||||
return errUserStatusNilPtr
|
||||
}
|
||||
*x = UserStatus(*v)
|
||||
case float64: // json marshals everything as a float64 if it's a number
|
||||
*x = UserStatus(v)
|
||||
case *float64: // json marshals everything as a float64 if it's a number
|
||||
if v == nil {
|
||||
return errUserStatusNilPtr
|
||||
}
|
||||
*x = UserStatus(*v)
|
||||
case *uint:
|
||||
if v == nil {
|
||||
return errUserStatusNilPtr
|
||||
}
|
||||
*x = UserStatus(*v)
|
||||
case *uint64:
|
||||
if v == nil {
|
||||
return errUserStatusNilPtr
|
||||
}
|
||||
*x = UserStatus(*v)
|
||||
case *string:
|
||||
if v == nil {
|
||||
return errUserStatusNilPtr
|
||||
}
|
||||
*x, err = ParseUserStatus(*v)
|
||||
if err != nil {
|
||||
// try parsing the integer value as a string
|
||||
if val, verr := strconv.Atoi(*v); verr == nil {
|
||||
*x, err = UserStatus(val), nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Value implements the driver Valuer interface.
|
||||
func (x UserStatus) Value() (driver.Value, error) {
|
||||
return int64(x), nil
|
||||
}
|
||||
|
||||
// Set implements the Golang flag.Value interface func.
|
||||
func (x *UserStatus) Set(val string) error {
|
||||
v, err := ParseUserStatus(val)
|
||||
*x = v
|
||||
return err
|
||||
}
|
||||
|
||||
// Get implements the Golang flag.Getter interface func.
|
||||
func (x *UserStatus) Get() interface{} {
|
||||
return *x
|
||||
}
|
||||
|
||||
// Type implements the github.com/spf13/pFlag Value interface.
|
||||
func (x *UserStatus) Type() string {
|
||||
return "UserStatus"
|
||||
}
|
||||
|
||||
type NullUserStatus struct {
|
||||
UserStatus UserStatus
|
||||
Valid bool
|
||||
}
|
||||
|
||||
func NewNullUserStatus(val interface{}) (x NullUserStatus) {
|
||||
x.Scan(val) // yes, we ignore this error, it will just be an invalid value.
|
||||
return
|
||||
}
|
||||
|
||||
// Scan implements the Scanner interface.
|
||||
func (x *NullUserStatus) Scan(value interface{}) (err error) {
|
||||
if value == nil {
|
||||
x.UserStatus, x.Valid = UserStatus(0), false
|
||||
return
|
||||
}
|
||||
|
||||
err = x.UserStatus.Scan(value)
|
||||
x.Valid = (err == nil)
|
||||
return
|
||||
}
|
||||
|
||||
// Value implements the driver Valuer interface.
|
||||
func (x NullUserStatus) Value() (driver.Value, error) {
|
||||
if !x.Valid {
|
||||
return nil, nil
|
||||
}
|
||||
// driver.Value accepts int64 for int values.
|
||||
return int64(x.UserStatus), nil
|
||||
}
|
||||
|
||||
type NullUserStatusStr struct {
|
||||
NullUserStatus
|
||||
}
|
||||
|
||||
func NewNullUserStatusStr(val interface{}) (x NullUserStatusStr) {
|
||||
x.Scan(val) // yes, we ignore this error, it will just be an invalid value.
|
||||
return
|
||||
}
|
||||
|
||||
// Value implements the driver Valuer interface.
|
||||
func (x NullUserStatusStr) Value() (driver.Value, error) {
|
||||
if !x.Valid {
|
||||
return nil, nil
|
||||
}
|
||||
return x.UserStatus.String(), nil
|
||||
}
|
||||
5
backend/database/fields/users.go
Normal file
5
backend/database/fields/users.go
Normal file
@@ -0,0 +1,5 @@
|
||||
package fields
|
||||
|
||||
// swagger:enum UserStatus
|
||||
// ENUM( Pending, Verified, Blocked)
|
||||
type UserStatus int16
|
||||
@@ -33,7 +33,6 @@ CREATE TABLE
|
||||
user_id INT8 NOT NULL,
|
||||
union_id VARCHAR(128),
|
||||
open_id VARCHAR(128) NOT NULL UNIQUE,
|
||||
access_key VARCHAR(256) NOT NULL default '',
|
||||
access_token VARCHAR(256) NOT NULL default '',
|
||||
refresh_token VARCHAR(256) NOT NULL default '',
|
||||
expire_at timestamp NOT NULL,
|
||||
|
||||
@@ -8,21 +8,22 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"backend/database/fields"
|
||||
"time"
|
||||
)
|
||||
|
||||
type UserOauths struct {
|
||||
ID int64 `sql:"primary_key" json:"id"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
DeletedAt *time.Time `json:"deleted_at"`
|
||||
Channel int16 `json:"channel"`
|
||||
UserID int64 `json:"user_id"`
|
||||
UnionID *string `json:"union_id"`
|
||||
OpenID string `json:"open_id"`
|
||||
AccessKey string `json:"access_key"`
|
||||
AccessToken string `json:"access_token"`
|
||||
RefreshToken string `json:"refresh_token"`
|
||||
ExpireAt time.Time `json:"expire_at"`
|
||||
Meta *string `json:"meta"`
|
||||
ID int64 `sql:"primary_key" json:"id"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
DeletedAt *time.Time `json:"deleted_at"`
|
||||
Channel fields.AuthChannel `json:"channel"`
|
||||
UserID int64 `json:"user_id"`
|
||||
UnionID *string `json:"union_id"`
|
||||
OpenID string `json:"open_id"`
|
||||
AccessKey string `json:"access_key"`
|
||||
AccessToken string `json:"access_token"`
|
||||
RefreshToken string `json:"refresh_token"`
|
||||
ExpireAt time.Time `json:"expire_at"`
|
||||
Meta *string `json:"meta"`
|
||||
}
|
||||
|
||||
@@ -5,7 +5,9 @@ ignores:
|
||||
- river_job
|
||||
- river_leader
|
||||
- river_queue
|
||||
# types:
|
||||
# users: # table name
|
||||
# meta: UserMeta
|
||||
# meta: Json[UserMeta]
|
||||
types:
|
||||
users: # table name
|
||||
status: UserStatus
|
||||
|
||||
user_oauths:
|
||||
channel: AuthChannel
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
package consts
|
||||
|
||||
// Format
|
||||
//
|
||||
// // swagger:enum CacheKey
|
||||
// // ENUM(
|
||||
// // VerifyCode = "code:__CHANNEL__:%s",
|
||||
// // )
|
||||
11
backend/pkg/oauth/contracts.go
Normal file
11
backend/pkg/oauth/contracts.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package oauth
|
||||
|
||||
import "time"
|
||||
|
||||
type OAuthInfo interface {
|
||||
GetOpenID() string
|
||||
GetUnionID() string
|
||||
GetAccessToken() string
|
||||
GetRefreshToken() string
|
||||
GetExpiredAt() time.Time
|
||||
}
|
||||
39
backend/pkg/oauth/wechat.go
Normal file
39
backend/pkg/oauth/wechat.go
Normal file
@@ -0,0 +1,39 @@
|
||||
package oauth
|
||||
|
||||
import "time"
|
||||
|
||||
var _ OAuthInfo = (*WechatOAuthInfo)(nil)
|
||||
|
||||
type WechatOAuthInfo struct {
|
||||
Scope string `json:"scope,omitempty"`
|
||||
OpenID string `json:"openid,omitempty"`
|
||||
UnionID string `json:"unionid,omitempty"`
|
||||
AccessToken string `json:"access_token,omitempty"`
|
||||
RefreshToken string `json:"refresh_token,omitempty"`
|
||||
ExpiresIn int64 `json:"expires_in,omitempty"`
|
||||
}
|
||||
|
||||
// GetAccessToken implements OAuthInfo.
|
||||
func (w *WechatOAuthInfo) GetAccessToken() string {
|
||||
return w.AccessToken
|
||||
}
|
||||
|
||||
// GetExpiredAt implements OAuthInfo.
|
||||
func (w *WechatOAuthInfo) GetExpiredAt() time.Time {
|
||||
return time.Now().Add(time.Duration(w.ExpiresIn) * time.Second)
|
||||
}
|
||||
|
||||
// GetOpenID implements OAuthInfo.
|
||||
func (w *WechatOAuthInfo) GetOpenID() string {
|
||||
return w.OpenID
|
||||
}
|
||||
|
||||
// GetRefreshToken implements OAuthInfo.
|
||||
func (w *WechatOAuthInfo) GetRefreshToken() string {
|
||||
return w.RefreshToken
|
||||
}
|
||||
|
||||
// GetUnionID implements OAuthInfo.
|
||||
func (w *WechatOAuthInfo) GetUnionID() string {
|
||||
return w.UnionID
|
||||
}
|
||||
@@ -1,10 +1,12 @@
|
||||
package events
|
||||
package event
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"git.ipao.vip/rogeecn/atom/container"
|
||||
"git.ipao.vip/rogeecn/atom/contracts"
|
||||
"git.ipao.vip/rogeecn/atom/utils/opt"
|
||||
"github.com/ThreeDotsLabs/watermill"
|
||||
"github.com/ThreeDotsLabs/watermill/message"
|
||||
)
|
||||
|
||||
@@ -46,3 +48,18 @@ func (ps *PubSub) Handle(
|
||||
) {
|
||||
ps.Router.AddHandler(handlerName, consumerTopic, ps.Subscriber, publisherTopic, ps.Publisher, handler)
|
||||
}
|
||||
|
||||
// publish
|
||||
func (ps *PubSub) Publish(e contracts.EventPublisher) error {
|
||||
if e == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
payload, err := e.Marshal()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
msg := message.NewMessage(watermill.NewUUID(), payload)
|
||||
return ps.Publisher.Publish(e.Topic(), msg)
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package events
|
||||
package event
|
||||
|
||||
import (
|
||||
"github.com/ThreeDotsLabs/watermill"
|
||||
@@ -1,4 +1,4 @@
|
||||
package events
|
||||
package event
|
||||
|
||||
import (
|
||||
"git.ipao.vip/rogeecn/atom/container"
|
||||
@@ -1,4 +1,4 @@
|
||||
package events
|
||||
package event
|
||||
|
||||
import (
|
||||
"git.ipao.vip/rogeecn/atom/container"
|
||||
@@ -1,4 +1,4 @@
|
||||
package events
|
||||
package event
|
||||
|
||||
import (
|
||||
"git.ipao.vip/rogeecn/atom/container"
|
||||
@@ -1,4 +1,4 @@
|
||||
package events
|
||||
package event
|
||||
|
||||
import (
|
||||
sqlDB "database/sql"
|
||||
@@ -51,4 +51,11 @@ func (c *Config) format() {
|
||||
c.Env = "unknown"
|
||||
}
|
||||
}
|
||||
|
||||
if c.EndpointGRPC == "" {
|
||||
c.EndpointGRPC = os.Getenv("OTEL_ENDPOINT_GRPC")
|
||||
if c.EndpointGRPC == "" {
|
||||
c.EndpointGRPC = "localhost:4317"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ var (
|
||||
)
|
||||
|
||||
func Start(ctx context.Context, spanName string, opts ...trace.SpanStartOption) (context.Context, trace.Span) {
|
||||
return tracer.Start(ctx, spanName, opts...)
|
||||
return tracer.Start(ctx, spanName)
|
||||
}
|
||||
|
||||
func Int64Counter(name string, options ...metric.Int64CounterOption) (metric.Int64Counter, error) {
|
||||
|
||||
Reference in New Issue
Block a user