Compare commits

..

5 Commits

Author SHA1 Message Date
eson
38375ffdda Join集群 2023-07-25 19:42:26 +08:00
eson
7200531c27 添加节点分布式 2023-07-25 19:32:51 +08:00
eson
a248c6cbeb TODO: Email Valid Url 2023-07-24 19:43:56 +08:00
eson
b43b5dbd59 fix 2023-07-24 19:17:02 +08:00
eson
a10e100364 save 2023-07-24 17:22:06 +08:00
68 changed files with 1439 additions and 268 deletions

135
fsm/fsm.go Normal file
View File

@ -0,0 +1,135 @@
package fsm
import (
"context"
"encoding/gob"
"fmt"
"fusenapi/model/gmodel"
"fusenapi/utils/auth"
"io"
"log"
"sync"
"time"
"github.com/hashicorp/raft"
"gorm.io/gorm"
)
// StateCluster is a simple key-value store as an FSM.
type StateCluster struct {
mu sync.Mutex
store map[int64]*UserState
gdb *gorm.DB
waiter *WaitCallback
ra *raft.Raft // The consensus mechanism
}
func (fsm *StateCluster) GetUserState(Userid int64) (*UserState, error) {
fsm.mu.Lock()
if us, ok := fsm.store[Userid]; ok {
// log.Println("exists")
fsm.mu.Unlock()
return us, nil
}
fsm.mu.Unlock()
// log.Println("apply")
cmd := &command{
Op: "update",
Key: Userid,
}
future := fsm.ra.Apply(cmd.Encode(), time.Minute)
// log.Println(future.Index())
if future.Response() != nil {
return nil, future.Response().(error)
}
if us := fsm.waiter.Wait(Userid, time.Second*5); us != nil {
return us, nil
} else {
return nil, fmt.Errorf("timeout")
}
}
// Apply applies a Raft log entry to the key-value store.
func (fsm *StateCluster) Apply(fsmlog *raft.Log) interface{} {
var cmd command
err := cmd.Decode(fsmlog.Data)
if err != nil {
return err
}
switch cmd.Op {
case "update":
fsm.mu.Lock()
defer fsm.mu.Unlock()
// log.Println("update")
models := gmodel.NewAllModels(fsm.gdb)
user, err := models.FsUser.FindUserById(context.TODO(), cmd.Key)
if err != nil {
log.Println(err)
}
userState := &UserState{
UserId: cmd.Key,
PwdHash: auth.StringToHash(*user.PasswordHash),
Expired: time.Now(),
}
fsm.store[cmd.Key] = userState
fsm.waiter.Done(userState)
default:
return fmt.Errorf("unrecognized command operation: %s", cmd.Op)
}
return nil
}
// Snapshot returns a snapshot of the key-value store.
func (fsm *StateCluster) Snapshot() (raft.FSMSnapshot, error) {
fsm.mu.Lock()
defer fsm.mu.Unlock()
snapshot := kvStoreSnapshot{
store: make(map[int64]*UserState),
}
for k, v := range fsm.store {
snapshot.store[k] = v
}
return &snapshot, nil
}
// Restore stores the key-value store to a previous state.
func (fsm *StateCluster) Restore(rc io.ReadCloser) error {
var snapshot kvStoreSnapshot
dec := gob.NewDecoder(rc)
if err := dec.Decode(&snapshot); err != nil {
return err
}
fsm.store = snapshot.store
return nil
}
// kvStoreSnapshot represents a snapshot of the key-value store.
type kvStoreSnapshot struct {
store map[int64]*UserState
}
// Persist writes the snapshot to the provided sink.
func (snapshot *kvStoreSnapshot) Persist(sink raft.SnapshotSink) error {
// The example has been simplified. In a production environment, you would
// need to handle this operation with more care.
return nil
}
// Release is invoked when the Raft instance is finished with the snapshot.
func (snapshot *kvStoreSnapshot) Release() {
// Normally you would put any cleanup here.
}

182
fsm/main.go Normal file
View File

@ -0,0 +1,182 @@
package fsm
import (
"bytes"
"encoding/gob"
"fmt"
"fusenapi/initalize"
"log"
"net"
"os"
"time"
"github.com/hashicorp/raft"
"gorm.io/gorm"
)
func test1() {
log.SetFlags(log.Llongfile)
fsm := StartNode("fs1", "localhost:5500", initalize.InitMysql("fusentest:XErSYmLELKMnf3Dh@tcp(110.41.19.98:3306)/fusentest"))
time.Sleep(time.Second * 5)
for i := 0; i < 30; i++ {
go log.Println(fsm.GetUserState(39))
}
log.Println(fsm.GetUserState(39))
select {}
}
// StartNode 启动节点
func StartNode(ServerID string, RaftBind string, gdb *gorm.DB) *StateCluster {
fsm := &StateCluster{
store: make(map[int64]*UserState),
waiter: NewWaitCallback(),
gdb: gdb,
}
var retainSnapshotCount = 2
// var ServerID string = "fs1"
// var RaftBind string = "localhost:5500"
var RaftDir string = fmt.Sprintf("/tmp/raftdir/%s", ServerID)
// Setup Raft configuration.
config := raft.DefaultConfig()
config.LocalID = raft.ServerID(ServerID)
// Setup Raft communication.
addr, err := net.ResolveTCPAddr("tcp", RaftBind)
if err != nil {
panic(err)
}
transport, err := raft.NewTCPTransport(RaftBind, addr, 3, 30*time.Second, os.Stderr)
if err != nil {
panic(err)
}
// Create the snapshot store. This allows the Raft to truncate the log.
snapshots, err := raft.NewFileSnapshotStore(RaftDir, retainSnapshotCount, os.Stderr)
if err != nil {
panic(fmt.Errorf("file snapshot store: %s", err))
}
// Create the log store and stable store.
logStore := raft.NewInmemStore()
stableStore := raft.NewInmemStore()
// Create the Raft system.
fsm.ra, err = raft.NewRaft(config, fsm, logStore, stableStore, snapshots, transport)
if err != nil {
panic(err)
}
configuration := raft.Configuration{
Servers: []raft.Server{
{
Suffrage: raft.Voter,
ID: config.LocalID,
Address: transport.LocalAddr(),
},
},
}
fu := fsm.ra.BootstrapCluster(configuration)
if err := fu.Error(); err != nil {
log.Println(err)
}
waitForLeader(fsm.ra)
return fsm
}
func JoinCluster(ServerID string, LeaderAddress string, RaftBind string, gdb *gorm.DB) *StateCluster {
fsm := StartNode(ServerID, RaftBind, gdb)
configFuture := fsm.ra.GetConfiguration()
if err := configFuture.Error(); err != nil {
log.Fatalf("failed to get raft configuration: %v", err)
}
for _, srv := range configFuture.Configuration().Servers {
if srv.ID == raft.ServerID(ServerID) && srv.Address == raft.ServerAddress(LeaderAddress) {
if future := fsm.ra.RemoveServer(srv.ID, 0, 0); future.Error() != nil {
log.Fatalf("Error removing existing server [%s]: %v", ServerID, future.Error())
}
}
}
f := fsm.ra.AddVoter(raft.ServerID(ServerID), raft.ServerAddress(RaftBind), 0, 0)
if f.Error() != nil {
log.Fatalf("Error adding voter: %v", f.Error())
}
return fsm
}
func waitForLeader(ra *raft.Raft) {
leaderCh := ra.LeaderCh()
for {
select {
case isLeader := <-leaderCh:
if isLeader {
return
}
case <-time.After(10 * time.Second):
log.Println("Still waiting for the leader...")
}
}
}
// var gdb *gorm.DB = initalize.InitMysql("fusentest:XErSYmLELKMnf3Dh@tcp(110.41.19.98:3306)/fusentest")
type UserState struct {
Expired time.Time
UserId int64
PwdHash uint64
}
func (us *UserState) Encode() []byte {
var buf = bytes.NewBuffer(nil)
err := gob.NewEncoder(buf).Encode(us)
if err != nil {
log.Panic(err)
return nil
}
return buf.Bytes()
}
// command is used for internal command representation.
type command struct {
Op string
Key int64
Value *UserState
}
func (cmd *command) Encode() []byte {
var buf = bytes.NewBuffer(nil)
err := gob.NewEncoder(buf).Encode(cmd)
if err != nil {
log.Panic(err)
return nil
}
return buf.Bytes()
}
func (cmd *command) Decode(sbuf []byte) error {
var buf = bytes.NewBuffer(sbuf)
err := gob.NewDecoder(buf).Decode(cmd)
if err != nil {
// log.Panic(err)
return err
}
return nil
}

7
fsm/main_test.go Normal file
View File

@ -0,0 +1,7 @@
package fsm
import "testing"
func TestMain(t *testing.T) {
test1()
}

57
fsm/waitcallback.go Normal file
View File

@ -0,0 +1,57 @@
package fsm
import (
"sync"
"time"
)
type condAndState struct {
cond *sync.Cond
state *UserState
}
type WaitCallback struct {
Waiter sync.Map
mu sync.Mutex
}
func NewWaitCallback() *WaitCallback {
return &WaitCallback{}
}
func (w *WaitCallback) Done(us *UserState) {
if v, ok := w.Waiter.Load(us.UserId); ok {
w.mu.Lock()
defer w.mu.Unlock()
cas := v.(*condAndState)
cas.state = us
cas.cond.Broadcast()
}
}
func (w *WaitCallback) Wait(key int64, timeout time.Duration) *UserState {
cas := &condAndState{
cond: sync.NewCond(&w.mu),
state: nil,
}
v, loaded := w.Waiter.LoadOrStore(key, cas)
if loaded {
cas = v.(*condAndState)
}
done := make(chan struct{})
go func() {
w.mu.Lock()
defer w.mu.Unlock()
cas.cond.Wait()
close(done)
}()
select {
case <-done:
return cas.state
case <-time.After(timeout):
return nil
}
}

6
go.mod
View File

@ -8,6 +8,7 @@ require (
github.com/bwmarrin/snowflake v0.3.0
github.com/golang-jwt/jwt v3.2.2+incompatible
github.com/google/uuid v1.3.0
github.com/hashicorp/raft v1.5.0
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
github.com/stripe/stripe-go/v74 v74.26.0
@ -22,6 +23,11 @@ require (
cloud.google.com/go/compute v1.20.1 // indirect
cloud.google.com/go/compute/metadata v0.2.3 // indirect
github.com/andybalholm/brotli v1.0.5 // indirect
github.com/armon/go-metrics v0.4.1 // indirect
github.com/hashicorp/go-hclog v1.5.0 // indirect
github.com/hashicorp/go-immutable-radix v1.0.0 // indirect
github.com/hashicorp/go-msgpack v0.5.5 // indirect
github.com/hashicorp/golang-lru v0.5.1 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/schollz/progressbar v1.0.0 // indirect
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.14.0 // indirect

85
go.sum
View File

@ -41,16 +41,25 @@ github.com/474420502/requests v1.40.0/go.mod h1:2SCVzim0ONFYG09g/GrM7RTeJIC6qTyZ
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60=
github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/SebastiaanKlippert/go-wkhtmltopdf v1.9.0 h1:DNrExYwvyyI404SxdUCCANAj9TwnGjRfa3cYFMNY1AU=
github.com/SebastiaanKlippert/go-wkhtmltopdf v1.9.0/go.mod h1:SQq4xfIdvf6WYKSDxAJc+xOJdolt+/bc1jnQKMtPMvQ=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a h1:HbKu58rmZpUGpz5+4FfNmIU+FmZg2P3Xaj2v2bfNWmk=
github.com/alicebob/miniredis/v2 v2.30.4 h1:8S4/o1/KoUArAGbGwPxcwf0krlzceva2XVOSchFS7Eo=
github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs=
github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/armon/go-metrics v0.4.1 h1:hR91U9KYmb6bLBYLQjyM+3j+rcd/UhE+G78SFnF8gJA=
github.com/armon/go-metrics v0.4.1/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+3JqfkOG4=
github.com/aws/aws-sdk-go v1.44.295 h1:SGjU1+MqttXfRiWHD6WU0DRhaanJgAFY+xIhEaugV8Y=
github.com/aws/aws-sdk-go v1.44.295/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bwmarrin/snowflake v0.3.0 h1:xm67bEhkKh6ij1790JB83OujPR5CzNe8QuQqAgISZN0=
@ -65,6 +74,8 @@ github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag=
github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
@ -86,6 +97,7 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.m
github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ=
github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=
github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
@ -93,6 +105,10 @@ github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeME
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0=
github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
@ -103,6 +119,8 @@ github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq
github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI=
github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
@ -150,6 +168,7 @@ github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
@ -170,8 +189,21 @@ github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0/go.mod h1:hgWBS7lorOAVIJEQMi4Zs
github.com/grpc-ecosystem/grpc-gateway/v2 v2.15.0 h1:1JYBfzqrWPcCclBwxFCPAou9n+q86mfnu7NAeHfte7A=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.15.0/go.mod h1:YDZoGHuwE+ov0c8smSH49WLF3F2LaWnYYuDVd+EWrc0=
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw=
github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-hclog v1.5.0 h1:bI2ocEMgcVlz55Oj1xZNBsVi900c7II+fWDyV9o+13c=
github.com/hashicorp/go-hclog v1.5.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
github.com/hashicorp/go-immutable-radix v1.0.0 h1:AKDB1HM5PWEA7i4nhcpwOrO2byshxBjXVn/J/3+z5/0=
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
github.com/hashicorp/go-msgpack v0.5.5 h1:i9R9JSrqIz0QVLz3sz+i3YJdT7TTSLcfLLzJi9aZTuI=
github.com/hashicorp/go-msgpack v0.5.5/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/raft v1.5.0 h1:uNs9EfJ4FwiArZRxxfd/dQ5d33nV31/CdCHArH89hT8=
github.com/hashicorp/raft v1.5.0/go.mod h1:pKHB2mf/Y25u3AHNSXVRv+yT+WAnmeTX0BwVppVQV+M=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
@ -181,22 +213,37 @@ github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9Y
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.15.15 h1:EF27CXIuDsYJ6mmvtBRlEuB2UVOqHG1tAXgZ7yIO+lw=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng=
github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ=
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
@ -204,19 +251,34 @@ github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE=
github.com/openzipkin/zipkin-go v0.4.1 h1:kNd/ST2yLLWhaWrkgchya40TJabe8Hioj9udfPcEO5A=
github.com/openzipkin/zipkin-go v0.4.1/go.mod h1:qY0VqDSN1pOBN94dBc6w2GJlWLiovAyg7Qt6/I9HecM=
github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY=
github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pelletier/go-toml/v2 v2.0.9 h1:uH2qQXheeefCCkuBBSLi7jCiSmj3VRh2+Goq2N7Xxu0=
github.com/pelletier/go-toml/v2 v2.0.9/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU=
github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8=
github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4=
github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w=
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4=
github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM=
github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg=
github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
@ -224,20 +286,27 @@ github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
github.com/schollz/progressbar v1.0.0 h1:gbyFReLHDkZo8mxy/dLWMr+Mpb1MokGJ1FqCiqacjZM=
github.com/schollz/progressbar v1.0.0/go.mod h1:/l9I7PC3L3erOuz54ghIRKUEFcosiWfLvJv+Eq26UMs=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0=
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stripe/stripe-go/v74 v74.26.0 h1:enbhLtjKGWvJKcGM0f2CazqFSXzpHXcQ42nG2PNsWK0=
@ -248,6 +317,7 @@ github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
@ -286,6 +356,7 @@ go.opentelemetry.io/proto/otlp v0.19.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI
go.uber.org/automaxprocs v1.5.2 h1:2LxUOGiR3O6tw8ui5sZa2LAaHnsviZdVOUZw4fvbnME=
go.uber.org/automaxprocs v1.5.2/go.mod h1:eRbA25aqJrxAbsLO0xy5jVwPt7FQnRgjW+efnwa1WM0=
go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
@ -326,6 +397,7 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
@ -333,6 +405,7 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
@ -375,9 +448,12 @@ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -387,6 +463,7 @@ golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -404,6 +481,9 @@ golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@ -566,14 +646,19 @@ google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQ
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/h2non/gock.v1 v1.1.2 h1:jBbHXgGBK/AoPVfJh5x4r/WxIrElvbLel8TCZkkZJoY=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=

View File

@ -3,6 +3,7 @@ package svc
import (
"errors"
"fmt"
"fusenapi/fsm"
"fusenapi/server/assistant/internal/config"
"net/http"
@ -14,18 +15,22 @@ import (
)
type ServiceContext struct {
Config config.Config
Config config.Config
SharedState *fsm.StateCluster
MysqlConn *gorm.DB
AllModels *gmodel.AllModelsGen
}
func NewServiceContext(c config.Config) *ServiceContext {
conn := initalize.InitMysql(c.SourceMysql)
StateServer := fsm.StartNode(c.Name, fmt.Sprintf("%s:%d", c.Host, c.Port-2000), conn)
return &ServiceContext{
Config: c,
MysqlConn: initalize.InitMysql(c.SourceMysql),
AllModels: gmodel.NewAllModels(initalize.InitMysql(c.SourceMysql)),
Config: c,
MysqlConn: conn,
SharedState: StateServer,
AllModels: gmodel.NewAllModels(initalize.InitMysql(c.SourceMysql)),
}
}

36
server/auth/auth.go Normal file
View File

@ -0,0 +1,36 @@
package main
import (
"flag"
"fmt"
"net/http"
"time"
"fusenapi/utils/auth"
"fusenapi/server/auth/internal/config"
"fusenapi/server/auth/internal/handler"
"fusenapi/server/auth/internal/svc"
"github.com/zeromicro/go-zero/core/conf"
"github.com/zeromicro/go-zero/rest"
)
var configFile = flag.String("f", "etc/auth.yaml", "the config file")
func main() {
flag.Parse()
var c config.Config
conf.MustLoad(*configFile, &c)
c.Timeout = int64(time.Second * 15)
server := rest.MustNewServer(c.RestConf, rest.WithCustomCors(auth.FsCors, func(w http.ResponseWriter) {
}))
defer server.Stop()
ctx := svc.NewServiceContext(c)
handler.RegisterHandlers(server, ctx)
fmt.Printf("Starting server at %s:%d...\n", c.Host, c.Port)
server.Start()
}

7
server/auth/auth_test.go Normal file
View File

@ -0,0 +1,7 @@
package main
import "testing"
func TestMain(t *testing.T) {
main()
}

18
server/auth/etc/auth.yaml Normal file
View File

@ -0,0 +1,18 @@
Name: auth
Host: 0.0.0.0
Port: 9980
MainAddress: "http://localhost:9900"
SourceMysql: fusentest:XErSYmLELKMnf3Dh@tcp(110.41.19.98:3306)/fusentest
Auth:
AccessSecret: fusen2023
AccessExpire: 2592000
RefreshAfter: 1592000
OAuth:
google:
appid: "1064842923358-e94msq2glj6qr4lrva9ts3hqjjt53q8h.apps.googleusercontent.com"
secret: "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"
facebook:
appid: "1095953604597065"
secret: "b146872550a190d5275b1420c212002e"

View File

@ -0,0 +1,27 @@
package config
import (
"fusenapi/server/auth/internal/types"
"github.com/zeromicro/go-zero/rest"
)
type Config struct {
rest.RestConf
SourceMysql string
Auth types.Auth
MainAddress string
OAuth struct {
Google struct {
Appid string
Secret string
}
Facebook struct {
Appid string
Secret string
}
}
}

View File

@ -6,9 +6,9 @@ import (
"fusenapi/utils/basic"
"fusenapi/server/home-user-auth/internal/logic"
"fusenapi/server/home-user-auth/internal/svc"
"fusenapi/server/home-user-auth/internal/types"
"fusenapi/server/auth/internal/logic"
"fusenapi/server/auth/internal/svc"
"fusenapi/server/auth/internal/types"
)
func AcceptCookieHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {

View File

@ -0,0 +1,37 @@
// Code generated by goctl. DO NOT EDIT.
package handler
import (
"net/http"
"fusenapi/server/auth/internal/svc"
"github.com/zeromicro/go-zero/rest"
)
func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
server.AddRoutes(
[]rest.Route{
{
Method: http.MethodPost,
Path: "/api/auth/login",
Handler: UserLoginHandler(serverCtx),
},
{
Method: http.MethodPost,
Path: "/api/auth/accept-cookie",
Handler: AcceptCookieHandler(serverCtx),
},
{
Method: http.MethodGet,
Path: "/api/auth/oauth2/login/google",
Handler: UserGoogleLoginHandler(serverCtx),
},
{
Method: http.MethodGet,
Path: "/api/auth/oauth2/register",
Handler: UserEmailRegisterHandler(serverCtx),
},
},
)
}

View File

@ -6,9 +6,9 @@ import (
"fusenapi/utils/basic"
"fusenapi/server/home-user-auth/internal/logic"
"fusenapi/server/home-user-auth/internal/svc"
"fusenapi/server/home-user-auth/internal/types"
"fusenapi/server/auth/internal/logic"
"fusenapi/server/auth/internal/svc"
"fusenapi/server/auth/internal/types"
)
func UserEmailRegisterHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {

View File

@ -6,9 +6,9 @@ import (
"fusenapi/utils/basic"
"fusenapi/server/home-user-auth/internal/logic"
"fusenapi/server/home-user-auth/internal/svc"
"fusenapi/server/home-user-auth/internal/types"
"fusenapi/server/auth/internal/logic"
"fusenapi/server/auth/internal/svc"
"fusenapi/server/auth/internal/types"
)
func UserGoogleLoginHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {

View File

@ -6,9 +6,9 @@ import (
"fusenapi/utils/basic"
"fusenapi/server/home-user-auth/internal/logic"
"fusenapi/server/home-user-auth/internal/svc"
"fusenapi/server/home-user-auth/internal/types"
"fusenapi/server/auth/internal/logic"
"fusenapi/server/auth/internal/svc"
"fusenapi/server/auth/internal/types"
)
func UserLoginHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {

View File

@ -6,8 +6,8 @@ import (
"context"
"fusenapi/server/home-user-auth/internal/svc"
"fusenapi/server/home-user-auth/internal/types"
"fusenapi/server/auth/internal/svc"
"fusenapi/server/auth/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
@ -26,6 +26,15 @@ func NewAcceptCookieLogic(ctx context.Context, svcCtx *svc.ServiceContext) *Acce
}
}
// 处理进入前逻辑w,r
// func (l *AcceptCookieLogic) BeforeLogic(w http.ResponseWriter, r *http.Request) {
// }
// 处理逻辑后 w,r 如:重定向, resp 必须重新处理
// func (l *AcceptCookieLogic) AfterLogic(w http.ResponseWriter, r *http.Request, resp *basic.Response) {
// // httpx.OkJsonCtx(r.Context(), w, resp)
// }
func (l *AcceptCookieLogic) AcceptCookie(req *types.Request, userinfo *auth.UserInfo) (resp *basic.Response) {
// 返回值必须调用Set重新返回, resp可以空指针调用 resp.SetStatus(basic.CodeOK, data)
// userinfo 传入值时, 一定不为null

View File

@ -0,0 +1,168 @@
package logic
import (
"bytes"
"log"
"net/smtp"
"sync"
"text/template"
"time"
)
var EmailManager *EmailSender
// EmailSender
type EmailSender struct {
lock sync.Mutex
EmailTasks chan string
Auth smtp.Auth
FromEmail string
emailSending map[string]*EmailTask
ResendTimeLimit time.Duration
semaphore chan struct{}
}
// EmailTask
type EmailTask struct {
Email string // email
SendTime time.Time // 处理的任务时间
}
func (m *EmailSender) ProcessEmailTasks() {
for {
emailTarget, ok := <-m.EmailTasks
if !ok {
log.Println("Email task channel closed")
break
}
m.lock.Lock()
_, isSending := m.emailSending[emailTarget]
if isSending {
m.lock.Unlock()
continue
}
m.emailSending[emailTarget] = &EmailTask{
Email: emailTarget,
SendTime: time.Now(),
}
m.lock.Unlock()
// Acquire a token
m.semaphore <- struct{}{}
go func() {
defer func() { <-m.semaphore }() // Release a token
content := RenderEmailTemplate("fusen", "http://www.baidu.com", "fusen@gmail.com", "mail-valid")
err := smtp.SendMail("smtp.gmail.com:587", m.Auth, m.FromEmail, []string{emailTarget}, content)
if err != nil {
log.Printf("Failed to send email to %s: %v\n", emailTarget, err)
m.Resend(emailTarget, content)
}
}()
}
}
// Resend 重发邮件
func (m *EmailSender) Resend(emailTarget string, content []byte) {
time.Sleep(m.ResendTimeLimit)
m.lock.Lock()
defer m.lock.Unlock()
// Check if the email task still exists and has not been sent successfully
if task, ok := m.emailSending[emailTarget]; ok && task.SendTime.Add(m.ResendTimeLimit).After(time.Now()) {
err := smtp.SendMail(emailTarget, m.Auth, m.FromEmail, []string{emailTarget}, content)
if err != nil {
log.Printf("Failed to resend email to %s: %v\n", emailTarget, err)
} else {
delete(m.emailSending, emailTarget)
}
}
}
// ClearExpiredTasks 清除过期的邮件任务
func (m *EmailSender) ClearExpiredTasks() {
ticker := time.NewTicker(time.Minute)
defer ticker.Stop()
for {
<-ticker.C
m.lock.Lock()
for email, task := range m.emailSending {
if task.SendTime.Add(m.ResendTimeLimit).Before(time.Now()) {
delete(m.emailSending, email)
}
}
m.lock.Unlock()
}
}
func init() {
// Initialize the email manager
EmailManager = &EmailSender{
EmailTasks: make(chan string, 10),
Auth: smtp.PlainAuth(
"",
"user@example.com",
"password",
"smtp.gmail.com",
),
FromEmail: "user@example.com",
emailSending: make(map[string]*EmailTask, 10),
ResendTimeLimit: time.Minute * 1,
semaphore: make(chan struct{}, 10), // Initialize semaphore with a capacity of 10
}
// Start processing email tasks
go EmailManager.ProcessEmailTasks()
// Start clearing expired tasks
go EmailManager.ClearExpiredTasks()
}
const emailTemplate = `Subject: Your {{.CompanyName}} Account Confirmation
Dear
Thank you for creating an account with {{.CompanyName}}. We're excited to have you on board!
Before we get started, we just need to confirm that this is the right email address. Please confirm your email address by clicking on the link below:
{{.ConfirmationLink}}
Once you've confirmed, you can get started with {{.CompanyName}}. If you have any questions, feel free to reply to this email. We're here to help!
If you did not create an account with us, please ignore this email.
Thanks,
{{.SenderName}}
{{.SenderTitle}}
{{.CompanyName}}
`
func RenderEmailTemplate(companyName, confirmationLink, senderName, senderTitle string) []byte {
tmpl, err := template.New("email").Parse(emailTemplate)
if err != nil {
log.Fatal(err)
}
data := map[string]string{
"CompanyName": companyName,
"ConfirmationLink": confirmationLink,
"SenderName": senderName,
"SenderTitle": senderTitle,
}
var result bytes.Buffer
err = tmpl.Execute(&result, data)
if err != nil {
log.Fatal(err)
}
return result.Bytes()
}

View File

@ -6,8 +6,8 @@ import (
"context"
"fusenapi/server/home-user-auth/internal/svc"
"fusenapi/server/home-user-auth/internal/types"
"fusenapi/server/auth/internal/svc"
"fusenapi/server/auth/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
@ -30,8 +30,9 @@ func NewUserEmailRegisterLogic(ctx context.Context, svcCtx *svc.ServiceContext)
// func (l *UserEmailRegisterLogic) BeforeLogic(w http.ResponseWriter, r *http.Request) {
// }
// 处理逻辑后 w,r 如:重定向
// 处理逻辑后 w,r 如:重定向, resp 必须重新处理
// func (l *UserEmailRegisterLogic) AfterLogic(w http.ResponseWriter, r *http.Request, resp *basic.Response) {
// // httpx.OkJsonCtx(r.Context(), w, resp)
// }
func (l *UserEmailRegisterLogic) UserEmailRegister(req *types.RequestEmailRegister, userinfo *auth.UserInfo) (resp *basic.Response) {

View File

@ -6,16 +6,16 @@ import (
"fusenapi/utils/basic"
"log"
"net/http"
"net/url"
"time"
"context"
"fusenapi/server/home-user-auth/internal/svc"
"fusenapi/server/home-user-auth/internal/types"
"fusenapi/server/auth/internal/svc"
"fusenapi/server/auth/internal/types"
"github.com/474420502/requests"
"github.com/zeromicro/go-zero/core/logx"
"github.com/zeromicro/go-zero/rest/httpx"
"golang.org/x/net/proxy"
"golang.org/x/oauth2"
"golang.org/x/oauth2/google"
@ -31,6 +31,7 @@ type UserGoogleLoginLogic struct {
isRegistered bool // 是否注册
registerToken string // 注册邮箱的token
oauthinfo *auth.OAuthInfo
}
func NewUserGoogleLoginLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UserGoogleLoginLogic {
@ -41,35 +42,59 @@ func NewUserGoogleLoginLogic(ctx context.Context, svcCtx *svc.ServiceContext) *U
}
}
// 处理进入前逻辑w,r
// func (l *UserGoogleLoginLogic) BeforeLogic(w http.ResponseWriter, r *http.Request) {
// log.Println(r, w)
// }
// 处理逻辑后 w,r 如:重定向, resp 必须重新处理
func (l *UserGoogleLoginLogic) AfterLogic(w http.ResponseWriter, r *http.Request, resp *basic.Response) {
rurl := fmt.Sprintf(
l.svcCtx.Config.MainAddress+"/oauth?token=%s&is_registered=%t&register_token=%s",
l.token,
l.isRegistered,
l.registerToken,
)
if resp.Code == 200 {
html := fmt.Sprintf(`
<!DOCTYPE html>
<html>
<head>
<title>Redirect</title>
<script type="text/javascript">
window.onload = function() {
window.location = "%s";
if !l.isRegistered {
now := time.Now()
rtoken, err := auth.GenerateRegisterToken(
&l.svcCtx.Config.Auth.AccessSecret,
l.svcCtx.Config.Auth.AccessExpire,
now.Unix(),
l.oauthinfo.Id,
l.oauthinfo.Platform,
)
if err != nil {
resp.SetStatus(basic.CodeOAuthRegisterTokenErr)
}
</script>
</head>
<body>
</body>
</html>
`, rurl)
fmt.Fprintln(w, html)
l.registerToken = rtoken
}
rurl := fmt.Sprintf(
l.svcCtx.Config.MainAddress+"/oauth?token=%s&is_registered=%t&register_token=%s",
l.token,
l.isRegistered,
l.registerToken,
)
html := fmt.Sprintf(`
<!DOCTYPE html>
<html>
<head>
<title>Redirect</title>
<script type="text/javascript">
window.onload = function() {
window.location = "%s";
}
</script>
</head>
<body>
</body>
</html>
`, rurl)
fmt.Fprintln(w, html)
} else {
httpx.OkJson(w, resp)
}
}
func (l *UserGoogleLoginLogic) UserGoogleLogin(req *types.RequestGoogleLogin, userinfo *auth.UserInfo) (resp *basic.Response) {
@ -114,36 +139,41 @@ func (l *UserGoogleLoginLogic) UserGoogleLogin(req *types.RequestGoogleLogin, us
log.Println(r.Json())
googleId := r.Json().Get("id").Int()
// l.redirectUrl = "http://localhost:9900/oauth?token=21321123&is_registered"
// return resp.Set(304, "21321321")
user, err := l.svcCtx.AllModels.FsUser.FindUserByGoogleId(context.TODO(), googleId)
log.Println(user)
if err != nil {
if err != gorm.ErrRecordNotFound {
logx.Error(err)
return resp.SetStatus(basic.CodeDbSqlErr)
}
// 进入邮件注册流程
if req.Email == "" {
return resp.SetStatus(basic.CodeOK)
}
// 如果密码匹配,则生成 JWT Token。
nowSec := time.Now().Unix()
jwtToken, err := auth.GenerateJwtToken(&l.svcCtx.Config.Auth.AccessSecret, l.svcCtx.Config.Auth.AccessExpire, nowSec, 0, 0)
// 这里是注册模块, 发邮件, 通过邮件注册确认邮箱存在
// 如果生成 JWT Token 失败,则抛出错误并返回未认证的状态码。
if err != nil {
logx.Error(err)
return resp.SetStatus(basic.CodeServiceErr)
// 邮箱验证格式错误
if !auth.ValidateEmail(req.Email) {
return resp.SetStatus(basic.CodeOAuthEmailErr)
}
return resp.SetRewriteHandler(func(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, "http://localhost:9900?token="+url.QueryEscape(jwtToken), http.StatusFound)
})
EmailManager.EmailTasks <- req.Email // email进入队
return resp.SetStatus(basic.CodeOK)
}
// 如果密码匹配,则生成 JWT Token。
nowSec := time.Now().Unix()
jwtToken, err := auth.GenerateJwtToken(&l.svcCtx.Config.Auth.AccessSecret, l.svcCtx.Config.Auth.AccessExpire, nowSec, user.Id, 0)
// 如果生成 JWT Token 失败,则抛出错误并返回未认证的状态码。
if err != nil {
logx.Error(err)
return resp.SetStatus(basic.CodeServiceErr)
}
l.token = jwtToken
return resp.SetStatus(basic.CodeOK)
}

View File

@ -1,16 +1,17 @@
package logic
import (
"context"
"errors"
"fmt"
"fusenapi/utils/auth"
"fusenapi/utils/basic"
"net/http"
"time"
"fusenapi/server/home-user-auth/internal/svc"
"fusenapi/server/home-user-auth/internal/types"
"fusenapi/utils/auth"
"fusenapi/utils/basic"
"context"
"fusenapi/server/auth/internal/svc"
"fusenapi/server/auth/internal/types"
"github.com/zeromicro/go-zero/core/logx"
"github.com/zeromicro/go-zero/rest/httpx"
@ -33,6 +34,11 @@ func NewUserLoginLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UserLog
}
}
// 处理进入前逻辑w,r
// func (l *UserLoginLogic) BeforeLogic(w http.ResponseWriter, r *http.Request) {
// }
// 处理逻辑后 w,r 如:重定向, resp 必须重新处理
func (l *UserLoginLogic) AfterLogic(w http.ResponseWriter, r *http.Request, resp *basic.Response) {
if l.token != "" {
w.Header().Add("Authorization", fmt.Sprintf("Bearer %s", l.token))
@ -42,6 +48,9 @@ func (l *UserLoginLogic) AfterLogic(w http.ResponseWriter, r *http.Request, resp
}
func (l *UserLoginLogic) UserLogin(req *types.RequestUserLogin, userinfo *auth.UserInfo) (resp *basic.Response) {
// 返回值必须调用Set重新返回, resp可以空指针调用 resp.SetStatus(basic.CodeOK, data)
// userinfo 传入值时, 一定不为null
// 创建一个 FsUserModel 对象 m 并实例化之,该对象用于操作 MySQL 数据库中的用户数据表。
m := l.svcCtx.AllModels.FsUser
@ -59,6 +68,7 @@ func (l *UserLoginLogic) UserLogin(req *types.RequestUserLogin, userinfo *auth.U
// 如果密码匹配,则生成 JWT Token。
nowSec := time.Now().Unix()
jwtToken, err := auth.GenerateJwtToken(&l.svcCtx.Config.Auth.AccessSecret, l.svcCtx.Config.Auth.AccessExpire, nowSec, user.Id, 0)
// 如果生成 JWT Token 失败,则抛出错误并返回未认证的状态码。
@ -78,7 +88,6 @@ func (l *UserLoginLogic) UserLogin(req *types.RequestUserLogin, userinfo *auth.U
}
l.token = jwtToken
// 返回认证成功的状态码以及数据对象 data 和 JWT Token。
return resp.SetStatus(basic.CodeOK, data)
}

View File

@ -0,0 +1,66 @@
package svc
import (
"errors"
"fmt"
"fusenapi/fsm"
"fusenapi/server/auth/internal/config"
"net/http"
"fusenapi/initalize"
"fusenapi/model/gmodel"
"github.com/golang-jwt/jwt"
"gorm.io/gorm"
)
type ServiceContext struct {
Config config.Config
SharedState *fsm.StateCluster
MysqlConn *gorm.DB
AllModels *gmodel.AllModelsGen
}
func NewServiceContext(c config.Config) *ServiceContext {
conn := initalize.InitMysql(c.SourceMysql)
StateServer := fsm.StartNode(c.Name, fmt.Sprintf("localhost:%d", c.Port-2001), conn)
return &ServiceContext{
Config: c,
MysqlConn: conn,
SharedState: StateServer,
AllModels: gmodel.NewAllModels(initalize.InitMysql(c.SourceMysql)),
}
}
func (svcCtx *ServiceContext) ParseJwtToken(r *http.Request) (jwt.MapClaims, error) {
AuthKey := r.Header.Get("Authorization")
if AuthKey == "" {
return nil, nil
}
AuthKey = AuthKey[7:]
if len(AuthKey) <= 50 {
return nil, errors.New(fmt.Sprint("Error parsing token, len:", len(AuthKey)))
}
token, err := jwt.Parse(AuthKey, func(token *jwt.Token) (interface{}, error) {
// 检查签名方法是否为 HS256
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
}
// 返回用于验证签名的密钥
return []byte(svcCtx.Config.Auth.AccessSecret), nil
})
if err != nil {
return nil, errors.New(fmt.Sprint("Error parsing token:", err))
}
// 验证成功返回
if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid {
return claims, nil
}
return nil, errors.New(fmt.Sprint("Invalid token", err))
}

View File

@ -0,0 +1,101 @@
// Code generated by goctl. DO NOT EDIT.
package types
import (
"fusenapi/utils/basic"
)
type RequestUserLogin struct {
Email string `json:"email"`
Password string `json:"password"`
}
type RequestGoogleLogin struct {
Code string `form:"code"`
Scope string `form:"scope"`
AuthUser string `form:"authuser"`
Prompt string `form:"prompt"`
Email string `form:"email,optional"`
}
type RequestEmailRegister struct {
Email string `json:"email"`
RegisterToken string `json:"register_token"`
}
type DataUserLogin struct {
Token string `json:"token"` // 登录jwt token
}
type DataGuest struct {
Token string `json:"token"` // 登录jwt token
}
type Request struct {
}
type Response struct {
Code int `json:"code"`
Message string `json:"msg"`
Data interface{} `json:"data"`
}
type Auth struct {
AccessSecret string `json:"accessSecret"`
AccessExpire int64 `json:"accessExpire"`
RefreshAfter int64 `json:"refreshAfter"`
}
type File struct {
Filename string `fsfile:"filename"`
Header map[string][]string `fsfile:"header"`
Size int64 `fsfile:"size"`
Data []byte `fsfile:"data"`
}
type Meta struct {
TotalCount int64 `json:"totalCount"`
PageCount int64 `json:"pageCount"`
CurrentPage int `json:"currentPage"`
PerPage int `json:"perPage"`
}
// Set 设置Response的Code和Message值
func (resp *Response) Set(Code int, Message string) *Response {
return &Response{
Code: Code,
Message: Message,
}
}
// Set 设置整个Response
func (resp *Response) SetWithData(Code int, Message string, Data interface{}) *Response {
return &Response{
Code: Code,
Message: Message,
Data: Data,
}
}
// SetStatus 设置默认StatusResponse(内部自定义) 默认msg, 可以带data, data只使用一个参数
func (resp *Response) SetStatus(sr *basic.StatusResponse, data ...interface{}) *Response {
newResp := &Response{
Code: sr.Code,
}
if len(data) == 1 {
newResp.Data = data[0]
}
return newResp
}
// SetStatusWithMessage 设置默认StatusResponse(内部自定义) 非默认msg, 可以带data, data只使用一个参数
func (resp *Response) SetStatusWithMessage(sr *basic.StatusResponse, msg string, data ...interface{}) *Response {
newResp := &Response{
Code: sr.Code,
Message: msg,
}
if len(data) == 1 {
newResp.Data = data[0]
}
return newResp
}

View File

@ -1,4 +1,4 @@
package homeuserauthtest
package authtest
import (
"fmt"
@ -19,7 +19,7 @@ func TestAcceptCookieLogic(t *testing.T) {
ses := fstests.GetSesssion()
// 向服务器发送 GET 请求,获取 cookie 信息
resp, err = ses.Post(fmt.Sprintf("http://%s:%d/user/accept-cookie", cnf.Host, cnf.Port)).TestExecute(gserver)
resp, err = ses.Post(fmt.Sprintf("http://%s:%d/api/auth/accept-cookie", cnf.Host, cnf.Port)).TestExecute(gserver)
if err != nil {
t.Error(err)
}

40
server/auth/test/basic.go Normal file
View File

@ -0,0 +1,40 @@
package authtest
import (
"fmt"
"fusenapi/server/auth/internal/config"
"fusenapi/server/auth/internal/handler"
"fusenapi/server/auth/internal/svc"
"fusenapi/utils/fstests"
"log"
"runtime"
"github.com/zeromicro/go-zero/core/conf"
"github.com/zeromicro/go-zero/rest"
)
var cnf config.Config
var gserver *rest.Server
func init() {
log.SetFlags(log.Llongfile)
gserver = GetTestServer()
}
func GetTestServer() *rest.Server {
conf.MustLoad(fstests.GetEtcYamlPathAuto(), &cnf)
server := rest.MustNewServer(cnf.RestConf)
runtime.SetFinalizer(server, func(server *rest.Server) {
if server != nil {
server.Stop()
}
})
ctx := svc.NewServiceContext(cnf)
handler.RegisterHandlers(server, ctx)
fmt.Printf("Starting server at %s:%d...\n", cnf.Host, cnf.Port)
return server
}

View File

@ -3,6 +3,7 @@ package svc
import (
"errors"
"fmt"
"fusenapi/fsm"
"fusenapi/server/backend/internal/config"
"net/http"
"time"
@ -15,7 +16,8 @@ import (
)
type ServiceContext struct {
Config config.Config
Config config.Config
SharedState *fsm.StateCluster
MysqlConn *gorm.DB
AllModels *gmodel.AllModelsGen

View File

@ -2,10 +2,11 @@ package backendtest
import (
"fmt"
authtest "fusenapi/server/auth/test"
"fusenapi/server/backend/internal/config"
"fusenapi/server/backend/internal/handler"
"fusenapi/server/backend/internal/svc"
homeuserauthtest "fusenapi/server/home-user-auth/test"
"fusenapi/utils/fstests"
"log"
"runtime"
@ -19,7 +20,7 @@ var userver, gserver *rest.Server
func init() {
log.SetFlags(log.Llongfile)
userver = homeuserauthtest.GetTestServer()
userver = authtest.GetTestServer()
gserver = GetTestServer()
}

View File

@ -16,7 +16,7 @@ func TestCaseQuotationDetail(t *testing.T) {
// }
// 向服务器发送 POST 请求,新增用户地址
tp := ses.Get(fmt.Sprintf("http://%s:%d//quotation/detail", cnf.Host, cnf.Port))
tp := ses.Get(fmt.Sprintf("http://%s:%d/api//quotation/detail", cnf.Host, cnf.Port))
tp.QueryParam("id").Set(1003)
resp, err := tp.TestExecute(gserver)
log.Println(resp.Json())

View File

@ -3,6 +3,7 @@ package svc
import (
"errors"
"fmt"
"fusenapi/fsm"
"fusenapi/server/canteen/internal/config"
"net/http"
@ -14,18 +15,22 @@ import (
)
type ServiceContext struct {
Config config.Config
Config config.Config
SharedState *fsm.StateCluster
MysqlConn *gorm.DB
AllModels *gmodel.AllModelsGen
}
func NewServiceContext(c config.Config) *ServiceContext {
conn := initalize.InitMysql(c.SourceMysql)
StateServer := fsm.StartNode(c.Name, fmt.Sprintf("%s:%d", c.Host, c.Port-2000), conn)
return &ServiceContext{
Config: c,
MysqlConn: initalize.InitMysql(c.SourceMysql),
AllModels: gmodel.NewAllModels(initalize.InitMysql(c.SourceMysql)),
Config: c,
MysqlConn: conn,
SharedState: StateServer,
AllModels: gmodel.NewAllModels(initalize.InitMysql(c.SourceMysql)),
}
}

View File

@ -3,6 +3,7 @@ package svc
import (
"errors"
"fmt"
"fusenapi/fsm"
"fusenapi/server/data-transfer/internal/config"
"net/http"
@ -14,18 +15,22 @@ import (
)
type ServiceContext struct {
Config config.Config
Config config.Config
SharedState *fsm.StateCluster
MysqlConn *gorm.DB
AllModels *gmodel.AllModelsGen
}
func NewServiceContext(c config.Config) *ServiceContext {
conn := initalize.InitMysql(c.SourceMysql)
StateServer := fsm.StartNode(c.Name, fmt.Sprintf("%s:%d", c.Host, c.Port-2000), conn)
return &ServiceContext{
Config: c,
MysqlConn: initalize.InitMysql(c.SourceMysql),
AllModels: gmodel.NewAllModels(initalize.InitMysql(c.SourceMysql)),
Config: c,
MysqlConn: conn,
SharedState: StateServer,
AllModels: gmodel.NewAllModels(initalize.InitMysql(c.SourceMysql)),
}
}

View File

@ -9,14 +9,5 @@ Auth:
AccessExpire: 2592000
RefreshAfter: 1592000
OAuth:
google:
appid: "1064842923358-e94msq2glj6qr4lrva9ts3hqjjt53q8h.apps.googleusercontent.com"
secret: "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"
facebook:
appid: "1095953604597065"
secret: "b146872550a190d5275b1420c212002e"
Stripe:
SK: "sk_test_51IisojHygnIJZeghPVSBhkwySfcyDV4SoAduIxu3J7bvSJ9cZMD96LY1LO6SpdbYquLJX5oKvgEBB67KT9pecfCy00iEC4pp9y"

View File

@ -7,6 +7,5 @@ import (
// var configFile = flag.String("f", "etc/home-user-auth.yaml", "the config file")
func TestMain(t *testing.T) {
// log.Println(model.RawFieldNames[FsCanteenType]())
main()
}

View File

@ -13,18 +13,6 @@ type Config struct {
MainAddress string
OAuth struct {
Google struct {
Appid string
Secret string
}
Facebook struct {
Appid string
Secret string
}
}
Stripe struct {
SK string
}

View File

@ -12,16 +12,6 @@ import (
func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
server.AddRoutes(
[]rest.Route{
{
Method: http.MethodPost,
Path: "/api/user/login",
Handler: UserLoginHandler(serverCtx),
},
{
Method: http.MethodPost,
Path: "/api/user/accept-cookie",
Handler: AcceptCookieHandler(serverCtx),
},
{
Method: http.MethodGet,
Path: "/api/user/fonts",
@ -67,16 +57,6 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
Path: "/api/user/order-delete",
Handler: UserOderDeleteHandler(serverCtx),
},
{
Method: http.MethodGet,
Path: "/api/user/oauth2/login/google",
Handler: UserGoogleLoginHandler(serverCtx),
},
{
Method: http.MethodGet,
Path: "/api/user/oauth2/login/register",
Handler: UserEmailRegisterHandler(serverCtx),
},
{
Method: http.MethodGet,
Path: "/api/user/order-list",

View File

@ -1,6 +1,7 @@
package svc
import (
"fusenapi/fsm"
"fusenapi/initalize"
"fusenapi/model/gmodel"
@ -14,18 +15,22 @@ import (
)
type ServiceContext struct {
Config config.Config
Config config.Config
SharedState *fsm.StateCluster
MysqlConn *gorm.DB
AllModels *gmodel.AllModelsGen
}
func NewServiceContext(c config.Config) *ServiceContext {
conn := initalize.InitMysql(c.SourceMysql)
StateServer := fsm.StartNode(c.Name, fmt.Sprintf("%s:%d", c.Host, c.Port-2000), conn)
return &ServiceContext{
Config: c,
MysqlConn: initalize.InitMysql(c.SourceMysql),
AllModels: gmodel.NewAllModels(initalize.InitMysql(c.SourceMysql)),
Config: c,
MysqlConn: conn,
SharedState: StateServer,
AllModels: gmodel.NewAllModels(initalize.InitMysql(c.SourceMysql)),
}
}

View File

@ -70,19 +70,6 @@ type Product struct {
IsStop int64 `json:"is_stop"`
}
type RequestGoogleLogin struct {
Code string `form:"code"`
Scope string `form:"scope"`
AuthUser string `form:"authuser"`
Prompt string `form:"prompt"`
Email string `form:"email,optional"`
}
type RequestEmailRegister struct {
Email string `json:"email"`
RegisterToken string `json:"register_token"`
}
type RequestContactService struct {
Type string `json:"type"` // 类型
RelationID int64 `json:"relation_id"` // 关系id
@ -108,11 +95,6 @@ type RequestBasicInfoForm struct {
IsRemoveBg int64 `json:"is_remove_bg"` // 用户上传logo是否去除背景
}
type RequestUserLogin struct {
Email string `json:"email"`
Password string `json:"password"`
}
type RequestAddAddress struct {
Id int64 `json:"id"` // address_id 地址id
IsDefault int64 `json:"is_default"` //是否默认
@ -133,14 +115,6 @@ type RequestOrderId struct {
RefundReason string `json:"refund_reason"` //取消原因
}
type DataUserLogin struct {
Token string `json:"token"` // 登录jwt token
}
type DataGuest struct {
Token string `json:"token"` // 登录jwt token
}
type DataUserBasicInfo struct {
Type int64 `json:"type"` // 1普通餐厅 2连锁餐厅
IsOrderStatusEmail int64 `json:"is_order_status_email"` // 订单状态改变时是否接收邮件

View File

@ -2,6 +2,7 @@ package homeuserauthtest
import (
"fmt"
authtest "fusenapi/server/auth/test"
"fusenapi/server/home-user-auth/internal/config"
"fusenapi/server/home-user-auth/internal/handler"
"fusenapi/server/home-user-auth/internal/svc"
@ -15,10 +16,12 @@ import (
)
var cnf config.Config
var userserver *rest.Server
var gserver *rest.Server
func init() {
log.SetFlags(log.Llongfile)
userserver = authtest.GetTestServer()
gserver = GetTestServer()
}

View File

@ -16,7 +16,7 @@ func TestCaseUserAddAddress(t *testing.T) {
var result gjson.Result
// 获取 session,并携带 JWT token
ses := fstests.GetSessionWithUserToken(t, gserver, cnf.Host, cnf.Port)
ses := fstests.GetSessionWithUserToken(t, userserver, cnf.Host, cnf.Port)
// 构建新增地址请求体
addrReq := types.RequestAddAddress{
@ -32,7 +32,7 @@ func TestCaseUserAddAddress(t *testing.T) {
}
// 向服务器发送 POST 请求,新增用户地址
tp := ses.Post(fmt.Sprintf("http://%s:%d/user/add-address", cnf.Host, cnf.Port))
tp := ses.Post(fmt.Sprintf("http://%s:%d/api/user/add-address", cnf.Host, cnf.Port))
tp.SetBodyJson(addrReq)
resp, err = tp.TestExecute(gserver)
if err != nil {
@ -85,7 +85,7 @@ func TestCaseUserAddAddress(t *testing.T) {
}
// 向服务器发送 POST 请求,新增用户地址
tp = ses.Post(fmt.Sprintf("http://%s:%d/user/add-address", cnf.Host, cnf.Port))
tp = ses.Post(fmt.Sprintf("http://%s:%d/api/user/add-address", cnf.Host, cnf.Port))
tp.SetBodyJson(addrReq)
resp, err = tp.TestExecute(gserver)
if err != nil {

View File

@ -15,10 +15,10 @@ func TestCaseAddressList(t *testing.T) {
var result gjson.Result
// 获取 session并携带 JWT token
ses := fstests.GetSessionWithUserToken(t, gserver, cnf.Host, cnf.Port)
ses := fstests.GetSessionWithUserToken(t, userserver, cnf.Host, cnf.Port)
// 向服务器发送 GET 请求,获取用户地址列表
resp, err = ses.Get(fmt.Sprintf("http://%s:%d/user/address-list", cnf.Host, cnf.Port)).TestExecute(gserver)
resp, err = ses.Get(fmt.Sprintf("http://%s:%d/api/user/address-list", cnf.Host, cnf.Port)).TestExecute(gserver)
if err != nil {
t.Error(err)
}

View File

@ -15,10 +15,10 @@ func TestCaseBasicInfoLogic(t *testing.T) {
var result gjson.Result
// 获取 session并携带 JWT token
ses := fstests.GetSessionWithUserToken(t, gserver, cnf.Host, cnf.Port)
ses := fstests.GetSessionWithUserToken(t, userserver, cnf.Host, cnf.Port)
// 向服务器发送 GET 请求,获取用户基本信息
resp, err = ses.Get(fmt.Sprintf("http://%s:%d/user/basic-info", cnf.Host, cnf.Port)).TestExecute(gserver)
resp, err = ses.Get(fmt.Sprintf("http://%s:%d/api/user/basic-info", cnf.Host, cnf.Port)).TestExecute(gserver)
if err != nil {
t.Error(err)
}

View File

@ -16,8 +16,8 @@ func TestUserContactService(t *testing.T) {
var result gjson.Result
// 获取 session并携带 JWT token
ses := fstests.GetSessionWithUserToken(t, gserver, cnf.Host, cnf.Port)
tp := ses.Post(fmt.Sprintf("http://%s:%d/user/contact-service", cnf.Host, cnf.Port))
ses := fstests.GetSessionWithUserToken(t, userserver, cnf.Host, cnf.Port)
tp := ses.Post(fmt.Sprintf("http://%s:%d/api/user/contact-service", cnf.Host, cnf.Port))
req := types.RequestContactService{Type: "order", RelationID: 481, Email: "9107058@qq.com", Name: "zhang"}
tp.SetBodyJson(req)
// 向服务器发送 GET 请求,获取用户基本信息

View File

@ -16,10 +16,10 @@ func TestCaseUserFontsLogic(t *testing.T) {
var result gjson.Result
// 获取 session并携带 JWT token
ses := fstests.GetSessionWithUserToken(t, gserver, cnf.Host, cnf.Port)
ses := fstests.GetSessionWithUserToken(t, userserver, cnf.Host, cnf.Port)
// 向服务器发送 GET 请求,获取字体列表
resp, err = ses.Get(fmt.Sprintf("http://%s:%d/user/fonts", cnf.Host, cnf.Port)).TestExecute(gserver)
resp, err = ses.Get(fmt.Sprintf("http://%s:%d/api/user/fonts", cnf.Host, cnf.Port)).TestExecute(gserver)
if err != nil {
t.Error(err)
}

View File

@ -15,10 +15,10 @@ func TestCaseGetTypeLogic(t *testing.T) {
var result gjson.Result
// 获取 session并携带 JWT token
ses := fstests.GetSessionWithUserToken(t, gserver, cnf.Host, cnf.Port)
ses := fstests.GetSessionWithUserToken(t, userserver, cnf.Host, cnf.Port)
// 向服务器发送 GET 请求,获取用户类型信息
resp, err = ses.Get(fmt.Sprintf("http://%s:%d/user/get-type", cnf.Host, cnf.Port)).TestExecute(gserver)
resp, err = ses.Get(fmt.Sprintf("http://%s:%d/api/user/get-type", cnf.Host, cnf.Port)).TestExecute(gserver)
if err != nil {
t.Error(err)
}

View File

@ -16,10 +16,10 @@ func TestCaseLogic(t *testing.T) {
var result gjson.Result
// 获取 session并携带 JWT token
ses := fstests.GetSessionWithUserToken(t, gserver, cnf.Host, cnf.Port)
ses := fstests.GetSessionWithUserToken(t, userserver, cnf.Host, cnf.Port)
// 向服务器发送 GET 请求,获取用户类型信息
tp := ses.Post(fmt.Sprintf("http://%s:%d/user/order-delete", cnf.Host, cnf.Port))
tp := ses.Post(fmt.Sprintf("http://%s:%d/api/user/order-delete", cnf.Host, cnf.Port))
reqO := types.RequestOrderId{
OrderId: 12,
}

View File

@ -17,10 +17,10 @@ func TestCaseUserSaveBasicinfoLogic(t *testing.T) {
var result gjson.Result
// 获取 session并携带 JWT token
ses := fstests.GetSessionWithUserToken(t, gserver, cnf.Host, cnf.Port)
ses := fstests.GetSessionWithUserToken(t, userserver, cnf.Host, cnf.Port)
var tp *requests.Temporary
tp = ses.Get(fmt.Sprintf("http://%s:%d/user/basic-info", cnf.Host, cnf.Port))
tp = ses.Get(fmt.Sprintf("http://%s:%d/api/user/basic-info", cnf.Host, cnf.Port))
resp, err = tp.TestExecute(gserver)
if err != nil {
t.Error(err)
@ -52,7 +52,7 @@ func TestCaseUserSaveBasicinfoLogic(t *testing.T) {
req.IsRemoveBg = ^req.IsRemoveBg + 2
// 向服务器发送 GET 请求,获取用户类型信息
tp = ses.Post(fmt.Sprintf("http://%s:%d/user/basic-info", cnf.Host, cnf.Port)).SetBodyJson(req)
tp = ses.Post(fmt.Sprintf("http://%s:%d/api/user/basic-info", cnf.Host, cnf.Port)).SetBodyJson(req)
resp, err = tp.TestExecute(gserver)
if err != nil {
t.Error(err)
@ -69,7 +69,7 @@ func TestCaseUserSaveBasicinfoLogic(t *testing.T) {
return
}
tp = ses.Get(fmt.Sprintf("http://%s:%d/user/basic-info", cnf.Host, cnf.Port))
tp = ses.Get(fmt.Sprintf("http://%s:%d/api/user/basic-info", cnf.Host, cnf.Port))
resp, err = tp.TestExecute(gserver)
if err != nil {
t.Error(err)

View File

@ -15,10 +15,10 @@ func TestCaseUserStatusConfigLogic(t *testing.T) {
var result gjson.Result
// 获取 session并携带 JWT token
ses := fstests.GetSessionWithUserToken(t, gserver, cnf.Host, cnf.Port)
ses := fstests.GetSessionWithUserToken(t, userserver, cnf.Host, cnf.Port)
// 向服务器发送 GET 请求,获取用户类型信息
resp, err = ses.Post(fmt.Sprintf("http://%s:%d/user/status-config", cnf.Host, cnf.Port)).TestExecute(gserver)
resp, err = ses.Post(fmt.Sprintf("http://%s:%d/api/user/status-config", cnf.Host, cnf.Port)).TestExecute(gserver)
if err != nil {
t.Error(err)
}

View File

@ -3,6 +3,7 @@ package svc
import (
"errors"
"fmt"
"fusenapi/fsm"
"fusenapi/server/inventory/internal/config"
"net/http"
@ -14,18 +15,22 @@ import (
)
type ServiceContext struct {
Config config.Config
Config config.Config
SharedState *fsm.StateCluster
MysqlConn *gorm.DB
AllModels *gmodel.AllModelsGen
}
func NewServiceContext(c config.Config) *ServiceContext {
conn := initalize.InitMysql(c.SourceMysql)
StateServer := fsm.StartNode(c.Name, fmt.Sprintf("%s:%d", c.Host, c.Port-2000), conn)
return &ServiceContext{
Config: c,
MysqlConn: initalize.InitMysql(c.SourceMysql),
AllModels: gmodel.NewAllModels(initalize.InitMysql(c.SourceMysql)),
Config: c,
MysqlConn: conn,
SharedState: StateServer,
AllModels: gmodel.NewAllModels(initalize.InitMysql(c.SourceMysql)),
}
}

View File

@ -3,6 +3,7 @@ package svc
import (
"errors"
"fmt"
"fusenapi/fsm"
"fusenapi/server/map-library/internal/config"
"net/http"
@ -14,18 +15,22 @@ import (
)
type ServiceContext struct {
Config config.Config
Config config.Config
SharedState *fsm.StateCluster
MysqlConn *gorm.DB
AllModels *gmodel.AllModelsGen
}
func NewServiceContext(c config.Config) *ServiceContext {
conn := initalize.InitMysql(c.SourceMysql)
StateServer := fsm.StartNode(c.Name, fmt.Sprintf("%s:%d", c.Host, c.Port-2000), conn)
return &ServiceContext{
Config: c,
MysqlConn: initalize.InitMysql(c.SourceMysql),
AllModels: gmodel.NewAllModels(initalize.InitMysql(c.SourceMysql)),
Config: c,
MysqlConn: conn,
SharedState: StateServer,
AllModels: gmodel.NewAllModels(initalize.InitMysql(c.SourceMysql)),
}
}

View File

@ -3,6 +3,7 @@ package svc
import (
"errors"
"fmt"
"fusenapi/fsm"
"fusenapi/server/orders/internal/config"
"net/http"
@ -14,18 +15,22 @@ import (
)
type ServiceContext struct {
Config config.Config
Config config.Config
SharedState *fsm.StateCluster
MysqlConn *gorm.DB
AllModels *gmodel.AllModelsGen
}
func NewServiceContext(c config.Config) *ServiceContext {
conn := initalize.InitMysql(c.SourceMysql)
StateServer := fsm.StartNode(c.Name, fmt.Sprintf("%s:%d", c.Host, c.Port-2000), conn)
return &ServiceContext{
Config: c,
MysqlConn: initalize.InitMysql(c.SourceMysql),
AllModels: gmodel.NewAllModels(initalize.InitMysql(c.SourceMysql)),
Config: c,
MysqlConn: conn,
SharedState: StateServer,
AllModels: gmodel.NewAllModels(initalize.InitMysql(c.SourceMysql)),
}
}

View File

@ -3,6 +3,7 @@ package svc
import (
"errors"
"fmt"
"fusenapi/fsm"
"fusenapi/server/product-model/internal/config"
"net/http"
@ -14,18 +15,22 @@ import (
)
type ServiceContext struct {
Config config.Config
Config config.Config
SharedState *fsm.StateCluster
MysqlConn *gorm.DB
AllModels *gmodel.AllModelsGen
}
func NewServiceContext(c config.Config) *ServiceContext {
conn := initalize.InitMysql(c.SourceMysql)
StateServer := fsm.StartNode(c.Name, fmt.Sprintf("%s:%d", c.Host, c.Port-2000), conn)
return &ServiceContext{
Config: c,
MysqlConn: initalize.InitMysql(c.SourceMysql),
AllModels: gmodel.NewAllModels(initalize.InitMysql(c.SourceMysql)),
Config: c,
MysqlConn: conn,
SharedState: StateServer,
AllModels: gmodel.NewAllModels(initalize.InitMysql(c.SourceMysql)),
}
}

View File

@ -3,6 +3,7 @@ package svc
import (
"errors"
"fmt"
"fusenapi/fsm"
"fusenapi/server/product-template/internal/config"
"net/http"
@ -14,18 +15,22 @@ import (
)
type ServiceContext struct {
Config config.Config
Config config.Config
SharedState *fsm.StateCluster
MysqlConn *gorm.DB
AllModels *gmodel.AllModelsGen
}
func NewServiceContext(c config.Config) *ServiceContext {
conn := initalize.InitMysql(c.SourceMysql)
StateServer := fsm.StartNode(c.Name, fmt.Sprintf("%s:%d", c.Host, c.Port-2000), conn)
return &ServiceContext{
Config: c,
MysqlConn: initalize.InitMysql(c.SourceMysql),
AllModels: gmodel.NewAllModels(initalize.InitMysql(c.SourceMysql)),
Config: c,
MysqlConn: conn,
SharedState: StateServer,
AllModels: gmodel.NewAllModels(initalize.InitMysql(c.SourceMysql)),
}
}

View File

@ -3,6 +3,7 @@ package svc
import (
"errors"
"fmt"
"fusenapi/fsm"
"fusenapi/server/product/internal/config"
"net/http"
@ -14,14 +15,14 @@ import (
)
type ServiceContext struct {
Config config.Config
Config config.Config
SharedState *fsm.StateCluster
MysqlConn *gorm.DB
AllModels *gmodel.AllModelsGen
}
func NewServiceContext(c config.Config) *ServiceContext {
return &ServiceContext{
Config: c,
MysqlConn: initalize.InitMysql(c.SourceMysql),

View File

@ -3,6 +3,7 @@ package svc
import (
"errors"
"fmt"
"fusenapi/fsm"
"fusenapi/server/render/internal/config"
"net/http"
@ -14,18 +15,22 @@ import (
)
type ServiceContext struct {
Config config.Config
Config config.Config
SharedState *fsm.StateCluster
MysqlConn *gorm.DB
AllModels *gmodel.AllModelsGen
}
func NewServiceContext(c config.Config) *ServiceContext {
conn := initalize.InitMysql(c.SourceMysql)
StateServer := fsm.StartNode(c.Name, fmt.Sprintf("%s:%d", c.Host, c.Port-2000), conn)
return &ServiceContext{
Config: c,
MysqlConn: initalize.InitMysql(c.SourceMysql),
AllModels: gmodel.NewAllModels(initalize.InitMysql(c.SourceMysql)),
Config: c,
MysqlConn: conn,
SharedState: StateServer,
AllModels: gmodel.NewAllModels(initalize.InitMysql(c.SourceMysql)),
}
}

View File

@ -3,6 +3,7 @@ package svc
import (
"errors"
"fmt"
"fusenapi/fsm"
"fusenapi/server/shopping-cart-confirmation/internal/config"
"net/http"
@ -14,18 +15,22 @@ import (
)
type ServiceContext struct {
Config config.Config
Config config.Config
SharedState *fsm.StateCluster
MysqlConn *gorm.DB
AllModels *gmodel.AllModelsGen
}
func NewServiceContext(c config.Config) *ServiceContext {
conn := initalize.InitMysql(c.SourceMysql)
StateServer := fsm.StartNode(c.Name, fmt.Sprintf("%s:%d", c.Host, c.Port-2000), conn)
return &ServiceContext{
Config: c,
MysqlConn: initalize.InitMysql(c.SourceMysql),
AllModels: gmodel.NewAllModels(initalize.InitMysql(c.SourceMysql)),
Config: c,
MysqlConn: conn,
SharedState: StateServer,
AllModels: gmodel.NewAllModels(initalize.InitMysql(c.SourceMysql)),
}
}

View File

@ -3,6 +3,7 @@ package svc
import (
"errors"
"fmt"
"fusenapi/fsm"
"fusenapi/server/upload/internal/config"
"net/http"
@ -17,7 +18,8 @@ import (
)
type ServiceContext struct {
Config config.Config
Config config.Config
SharedState *fsm.StateCluster
MysqlConn *gorm.DB
AllModels *gmodel.AllModelsGen

View File

@ -2,7 +2,8 @@ package test
import (
"fmt"
homeuserauthtest "fusenapi/server/home-user-auth/test"
authtest "fusenapi/server/auth/test"
"fusenapi/server/upload/internal/config"
"fusenapi/server/upload/internal/handler"
"fusenapi/server/upload/internal/svc"
@ -20,7 +21,7 @@ var userver, gserver *rest.Server
func init() {
log.SetFlags(log.Llongfile)
userver = homeuserauthtest.GetTestServer()
userver = authtest.GetTestServer()
gserver = GetTestServer()
}

View File

@ -19,7 +19,7 @@ func TestCaseRenderMegre(t *testing.T) {
// 获取 session并携带 JWT token
ses := fstests.GetSessionWithUserToken(t, userver, cnf.Host, cnf.Port)
tp := ses.Post(fmt.Sprintf("http://%s:%d/upload/upload-file-backend", cnf.Host, cnf.Port))
tp := ses.Post(fmt.Sprintf("http://%s:%d/api/upload/upload-file-backend", cnf.Host, cnf.Port))
mp := tp.CreateBodyMultipart()
f, err := os.Open("./fusen.webp")

View File

@ -20,7 +20,7 @@ func TestCasePersonalization(t *testing.T) {
// 获取 session并携带 JWT token
ses := fstests.GetSessionWithUserToken(t, userver, cnf.Host, cnf.Port)
tp := ses.Post(fmt.Sprintf("http://%s:%d/upload/upload-file-frontend", cnf.Host, cnf.Port))
tp := ses.Post(fmt.Sprintf("http://%s:%d/api/upload/upload-file-frontend", cnf.Host, cnf.Port))
data, err := ioutil.ReadFile("./fusen.webp")
if err != nil {
@ -75,7 +75,7 @@ func TestCaseRenderMegreF(t *testing.T) {
// 获取 session并携带 JWT token
ses := fstests.GetSessionWithUserToken(t, userver, cnf.Host, cnf.Port)
tp := ses.Post(fmt.Sprintf("http://%s:%d/upload/upload-file-frontend", cnf.Host, cnf.Port))
tp := ses.Post(fmt.Sprintf("http://%s:%d/api/upload/upload-file-frontend", cnf.Host, cnf.Port))
data, err := ioutil.ReadFile("./fusen.webp")
if err != nil {

View File

@ -3,6 +3,7 @@ package svc
import (
"errors"
"fmt"
"fusenapi/fsm"
"fusenapi/server/webset/internal/config"
"net/http"
@ -14,18 +15,22 @@ import (
)
type ServiceContext struct {
Config config.Config
Config config.Config
SharedState *fsm.StateCluster
MysqlConn *gorm.DB
AllModels *gmodel.AllModelsGen
}
func NewServiceContext(c config.Config) *ServiceContext {
conn := initalize.InitMysql(c.SourceMysql)
StateServer := fsm.StartNode(c.Name, fmt.Sprintf("%s:%d", c.Host, c.Port-2000), conn)
return &ServiceContext{
Config: c,
MysqlConn: initalize.InitMysql(c.SourceMysql),
AllModels: gmodel.NewAllModels(initalize.InitMysql(c.SourceMysql)),
Config: c,
MysqlConn: conn,
SharedState: StateServer,
AllModels: gmodel.NewAllModels(initalize.InitMysql(c.SourceMysql)),
}
}

View File

@ -2,7 +2,8 @@ package webset_test
import (
"fmt"
homeuserauthtest "fusenapi/server/home-user-auth/test"
authtest "fusenapi/server/auth/test"
"fusenapi/server/webset/internal/config"
"fusenapi/server/webset/internal/handler"
"fusenapi/server/webset/internal/svc"
@ -19,7 +20,7 @@ var userver, gserver *rest.Server
func init() {
log.SetFlags(log.Llongfile)
userver = homeuserauthtest.GetTestServer()
userver = authtest.GetTestServer()
gserver = GetTestServer()
}

View File

@ -20,7 +20,7 @@ func TestWebSetLogic(t *testing.T) {
// 获取 session并携带 JWT token
ses := fstests.GetSesssion()
tp := ses.Get(fmt.Sprintf("http://%s:%d/web-set/setting", cnf.Host, cnf.Port))
tp := ses.Get(fmt.Sprintf("http://%s:%d/api/web-set/setting", cnf.Host, cnf.Port))
for _, tType := range testTypes {
tp.QueryParam("type").Set(tType)

53
server_api/auth.api Normal file
View File

@ -0,0 +1,53 @@
syntax = "v1"
info (
title: // TODO: add title
desc: // TODO: add description
author: ""
email: ""
)
import "basic.api"
service auth {
@handler UserLoginHandler
post /api/auth/login(RequestUserLogin) returns (response);
@handler AcceptCookieHandler
post /api/auth/accept-cookie(request) returns (response);
@handler UserGoogleLoginHandler
get /api/auth/oauth2/login/google(RequestGoogleLogin) returns (response);
@handler UserEmailRegisterHandler
get /api/auth/oauth2/register(RequestEmailRegister) returns (response);
}
// UserAddAddressHandler 用户登录请求结构
type RequestUserLogin {
Email string `json:"email"`
Password string `json:"password"`
}
type RequestGoogleLogin {
Code string `form:"code"`
Scope string `form:"scope"`
AuthUser string `form:"authuser"`
Prompt string `form:"prompt"`
Email string `form:"email,optional"`
}
type RequestEmailRegister {
Email string `json:"email"`
RegisterToken string `json:"register_token"`
}
// UserLoginHandler 用户登录请求结构
type DataUserLogin {
Token string `json:"token"` // 登录jwt token
}
// DataGuest 游客获取toekn请求结构
type DataGuest {
Token string `json:"token"` // 登录jwt token
}

View File

@ -14,12 +14,6 @@ service home-user-auth {
// @handler UserRegisterHandler
// post /api/user/register(RequestUserRegister) returns (response);
@handler UserLoginHandler
post /api/user/login(RequestUserLogin) returns (response);
@handler AcceptCookieHandler
post /api/user/accept-cookie(request) returns (response);
@handler UserFontsHandler
get /api/user/fonts(request) returns (response);
@ -50,12 +44,6 @@ service home-user-auth {
@handler UserOderDeleteHandler
post /api/user/order-delete(RequestOrderId) returns (response);
@handler UserGoogleLoginHandler
get /api/user/oauth2/login/google(RequestGoogleLogin) returns (response);
@handler UserEmailRegisterHandler
get /api/user/oauth2/login/register(RequestEmailRegister) returns (response);
//订单列表
@handler UserOrderListHandler
get /api/user/order-list (UserOrderListReq) returns (response);
@ -136,19 +124,6 @@ type Product {
IsStop int64 `json:"is_stop"`
}
type RequestGoogleLogin {
Code string `form:"code"`
Scope string `form:"scope"`
AuthUser string `form:"authuser"`
Prompt string `form:"prompt"`
Email string `form:"email,optional"`
}
type RequestEmailRegister {
Email string `json:"email"`
RegisterToken string `json:"register_token"`
}
type RequestContactService {
Type string `json:"type"` // 类型
RelationID int64 `json:"relation_id"` // 关系id
@ -176,12 +151,6 @@ type RequestBasicInfoForm {
// NewPassword string `form:"new_password,optional" db:"new_password"` // new_password 如果存在新密码
}
// UserAddAddressHandler 用户登录请求结构
type RequestUserLogin {
Email string `json:"email"`
Password string `json:"password"`
}
// RequestAddAddress 增加地址结构
type RequestAddAddress {
Id int64 `json:"id"` // address_id 地址id
@ -204,16 +173,6 @@ type RequestOrderId {
RefundReason string `json:"refund_reason"` //取消原因
}
// UserLoginHandler 用户登录请求结构
type DataUserLogin {
Token string `json:"token"` // 登录jwt token
}
// DataGuest 游客获取toekn请求结构
type DataGuest {
Token string `json:"token"` // 登录jwt token
}
// UserBasicInfoHandler 返回data结构
type DataUserBasicInfo {
Type int64 `json:"type"` // 1普通餐厅 2连锁餐厅

View File

@ -1,9 +1,51 @@
package auth
import (
"crypto/sha256"
"encoding/binary"
"errors"
"fmt"
"net/http"
"net/mail"
"github.com/golang-jwt/jwt"
)
func ParseJwtTokenUint64Secret(r *http.Request, AccessSecret uint64) (jwt.MapClaims, error) {
AuthKey := r.Header.Get("Authorization")
if AuthKey == "" {
return nil, nil
}
AuthKey = AuthKey[7:]
if len(AuthKey) <= 50 {
return nil, errors.New(fmt.Sprint("Error parsing token, len:", len(AuthKey)))
}
// Convert uint64 to []byte
key := make([]byte, 8)
binary.BigEndian.PutUint64(key, AccessSecret)
token, err := jwt.Parse(AuthKey, func(token *jwt.Token) (interface{}, error) {
// 检查签名方法是否为 HS256
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
}
// 返回用于验证签名的密钥
return key, nil
})
if err != nil {
return nil, errors.New(fmt.Sprint("Error parsing token:", err))
}
// 验证成功返回
if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid {
return claims, nil
}
return nil, errors.New(fmt.Sprint("Invalid token", err))
}
// ValidateEmail checks if the provided string is a valid email address.
func ValidateEmail(email string) bool {
_, err := mail.ParseAddress(email)
@ -17,6 +59,14 @@ func ValidatePassword(password string) bool {
return len(password) >= minPasswordLength
}
func StringToHash(s string) uint64 {
hash := sha256.New()
hash.Write([]byte(s))
hashed := hash.Sum(nil)
intHash := binary.BigEndian.Uint64(hashed)
return intHash
}
var secret = []byte("your-secret")
// func generateConfirmationLink(id, email, password, name string, platform string) (string, error) {

View File

@ -68,6 +68,11 @@ type BackendUserInfo struct {
DepartmentId int64 `json:"department_id"`
}
type OAuthInfo struct {
Id int64 `json:"id"`
Platform string `json:"platform"`
}
// 获取登录信息
func GetUserInfoFormMapClaims(claims jwt.MapClaims) (*UserInfo, error) {
userinfo := &UserInfo{}
@ -195,3 +200,79 @@ func CheckValueRange[T comparable](v T, rangevalues ...T) bool {
}
return false
}
// GenerateRegisterToken 网站注册 token生成
func GenerateRegisterToken(accessSecret *string, accessExpire, nowSec int64, id int64, platform string) (string, error) {
claims := make(jwt.MapClaims)
claims["exp"] = nowSec + accessExpire
claims["iat"] = nowSec
if id == 0 {
err := errors.New("userid and guestid cannot be 0 at the same time")
logx.Error(err)
return "", err
}
claims["id"] = id
claims["platform"] = platform
token := jwt.New(jwt.SigningMethodHS256)
token.Claims = claims
return token.SignedString([]byte(*accessSecret))
}
// GetRegisterFormMapClaims 获取注册唯一token标识登录信息
func GetRegisterFormMapClaims(claims jwt.MapClaims) (*OAuthInfo, error) {
oauthinfo := &OAuthInfo{}
if userid, ok := claims["id"]; ok {
uid, ok := userid.(float64)
if !ok {
err := errors.New(fmt.Sprint("parse uid form context err:", userid))
logx.Error("parse uid form context err:", err)
return nil, err
}
oauthinfo.Id = int64(uid)
} else {
err := errors.New(`id not in claims`)
logx.Error(`id not in claims`)
return nil, err
}
if splatform, ok := claims["id"]; ok {
platform, ok := splatform.(string)
if !ok {
err := errors.New(fmt.Sprint("parse uid form context err:", platform))
logx.Error("parse uid form context err:", err)
return nil, err
}
oauthinfo.Platform = platform
} else {
err := errors.New(`id not in claims`)
logx.Error(`id not in claims`)
return nil, err
}
return oauthinfo, nil
}
func getRegisterJwtClaims(Token string, AccessSecret *string) (jwt.MapClaims, error) {
token, err := jwt.Parse(Token, func(token *jwt.Token) (interface{}, error) {
// 检查签名方法是否为 HS256
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
}
// 返回用于验证签名的密钥
return []byte(*AccessSecret), nil
})
if err != nil {
return nil, errors.New(fmt.Sprint("Error parsing token:", err))
}
// 验证成功返回
if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid {
return claims, nil
}
return nil, errors.New(fmt.Sprint("Invalid token", err))
}

View File

@ -39,7 +39,9 @@ var (
CodeServiceErr = &StatusResponse{510, "server logic error"} // 服务逻辑错误
CodeUnAuth = &StatusResponse{401, "unauthorized"} // 未授权
CodeOAuthGoogleApiErr = &StatusResponse{5070, "oauth2 google api error"}
CodeOAuthGoogleApiErr = &StatusResponse{5070, "oauth2 google api error"}
CodeOAuthRegisterTokenErr = &StatusResponse{5071, "oauth2 jwt token error"}
CodeOAuthEmailErr = &StatusResponse{5071, "Invalid email format"}
CodeS3PutObjectRequestErr = &StatusResponse{5060, "s3 PutObjectRequest error"} // s3 PutObjectRequest 错误
CodeS3PutSizeLimitErr = &StatusResponse{5061, "s3 over limit size error"} // s3 超过文件大小限制 错误

View File

@ -52,6 +52,8 @@ func RequestParse(w http.ResponseWriter, r *http.Request, svcCtx IJWTParse, Logi
// 解析JWT token,并对空用户进行判断
claims, err := svcCtx.ParseJwtToken(r)
// auth.ParseJwtTokenUint64Secret()
// 如果解析JWT token出错,则返回未授权的JSON响应并记录错误消息
if err != nil {
httpx.OkJsonCtx(r.Context(), w, &Response{

View File

@ -85,10 +85,10 @@ func GetSesssion() *requests.Session {
func GetSessionWithUserToken(t *testing.T, server requests.ITestServer, Host string, Port int) *requests.Session {
ses := requests.NewSession()
tp := ses.Post(fmt.Sprintf("http://%s:%d/user/login", Host, Port))
tp := ses.Post(fmt.Sprintf("http://%s:%d/api/auth/login", Host, Port))
tp.SetBodyJson(map[string]interface{}{
"name": "9107058@qq.com",
"pwd": "$2y$13$2y4O4OIz/zcK5C0vlSc9LuSpjWySjInLBSe49yDkE.iURb.R1hDsy",
"email": "9107058@qq.com",
"password": "t1I0hOs0/AmhZfNk9ZiNnd3YZJpI+LL6COnHAmYEJk4=",
})
resp, err := tp.TestExecute(server)
if err != nil {
@ -104,17 +104,17 @@ func GetSessionWithUserToken(t *testing.T, server requests.ITestServer, Host str
if !token.Exists() {
t.Error("data.token is not exists")
}
ses.Header.Add("Authorization", token.String())
ses.Header.Add("Authorization", fmt.Sprintf("Bearer %s", token.String()))
return ses
}
func GetBackendSessionWithUserToken(t *testing.T, server requests.ITestServer, Host string, Port int) *requests.Session {
ses := requests.NewSession()
tp := ses.Post(fmt.Sprintf("http://%s:%d/backend-user/login", Host, Port))
tp := ses.Post(fmt.Sprintf("http://%s:%d/api/backend-user/login", Host, Port))
tp.SetBodyJson(map[string]interface{}{
"name": "admin@admin.com",
"pwd": "ZnVzZW5fYmFja2VuZF8yMDIz47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU=",
"email": "admin@admin.com",
"pwd": "ZnVzZW5fYmFja2VuZF8yMDIz47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU=",
})
resp, err := tp.TestExecute(server)
if err != nil {
@ -130,14 +130,14 @@ func GetBackendSessionWithUserToken(t *testing.T, server requests.ITestServer, H
if !token.Exists() {
t.Error("data.token is not exists")
}
ses.Header.Add("Authorization", token.String())
ses.Header.Add("Authorization", fmt.Sprintf("Bearer %s", token.String()))
return ses
}
func GetSesssionWithGuestToken(t *testing.T, server requests.ITestServer, Host string, Port int) *requests.Session {
ses := requests.NewSession()
tp := ses.Post(fmt.Sprintf("http://%s:%d/accept/cookie", Host, Port))
tp := ses.Post(fmt.Sprintf("http://%s:%d/api/auth/accept/cookie", Host, Port))
resp, err := tp.TestExecute(server)
if err != nil {
@ -148,7 +148,7 @@ func GetSesssionWithGuestToken(t *testing.T, server requests.ITestServer, Host s
if !token.Exists() {
t.Error("data.token is not exists")
}
ses.Header.Add("Authorization", token.String())
ses.Header.Add("Authorization", fmt.Sprintf("Bearer %s", token.String()))
return ses
}