diff --git a/Chat.go b/Chat.go index ef620b5..167e81f 100644 --- a/Chat.go +++ b/Chat.go @@ -3,6 +3,8 @@ package main import ( "context" "encoding/json" + "fmt" + "net/url" "sort" "strings" "time" @@ -19,7 +21,20 @@ func ChatPageHandler(c *fiber.Ctx) error { edgeClient = edgeClient.WithGlobals(map[string]interface{}{"ext::auth::client_token": authCookie}) } - return c.Render("chat", fiber.Map{"IsLogin": checkIfLogin(), "HaveKey": checkIfHaveKey(), "IsSubscribed": IsCurrentUserSubscribed(), "IsLimiteReached": IsCurrentUserLimiteReached()}, "layouts/main") + var ( + isSub bool + limitReach bool + ) + + if !checkIfLogin() { + isSub = false + limitReach = false + } else { + isSub = IsCurrentUserSubscribed() + limitReach = IsCurrentUserLimiteReached() + } + + return c.Render("chat", fiber.Map{"IsLogin": checkIfLogin(), "HaveKey": checkIfHaveKey(), "IsSubscribed": isSub, "IsLimiteReached": limitReach}, "layouts/main") } func DeleteMessageHandler(c *fiber.Ctx) error { @@ -276,9 +291,20 @@ func generateEnterKeyChatHTML() string { func generateLimitReachedChatHTML() string { welcomeMessage := `You have reached the maximum number of messages for a free account. Please upgrade your account to continue using JADE.` + var result User + err := edgeClient.QuerySingle(edgeCtx, "SELECT global currentUser { stripe_id, email } LIMIT 1;", &result) + if err != nil { + panic(err) + } + + clientSecretSession := CreateClientSecretSession() + + // TODO Replace by live API call stripeTable := ` - + ` htmlString := "
" @@ -518,7 +544,7 @@ func GenerateModelPopoverHTML(refresh bool) string { openaiExists, anthropicExists, mistralExists, groqExists, gooseaiExists, googleExists := getExistingKeys() var llms []LLM - err := edgeClient.Query(context.Background(), ` + err := edgeClient.Query(edgeCtx, ` SELECT LLM { id, name, @@ -573,6 +599,23 @@ func LoadSettingsHandler(c *fiber.Ctx) error { return c.SendString("") } + var user User + err := edgeClient.QuerySingle(edgeCtx, ` + SELECT User { + email + } + FILTER .id = global currentUser.id + `, &user) + if err != nil { + panic(err) + } + + // Percent encoding of the email + user.Email = url.QueryEscape(user.Email) + + stripeSubLink := "https://billing.stripe.com/p/login/test_eVa5kC1q7dogaaIcMM?prefilled_email=" + user.Email + fmt.Println(stripeSubLink) + openaiExists, anthropicExists, mistralExists, groqExists, gooseaiExists, googleExists := getExistingKeys() out, err := pongo2.Must(pongo2.FromFile("views/partials/popover-settings.html")).Execute(pongo2.Context{ @@ -585,6 +628,7 @@ func LoadSettingsHandler(c *fiber.Ctx) error { "GoogleExists": googleExists, "AnyExists": openaiExists || anthropicExists || mistralExists || groqExists || gooseaiExists || googleExists, "IsSub": IsCurrentUserSubscribed(), + "StripeSubLink": stripeSubLink, }) if err != nil { panic(err) diff --git a/Stripe.go b/Stripe.go index b47162e..c25783a 100644 --- a/Stripe.go +++ b/Stripe.go @@ -1,8 +1,16 @@ package main import ( + "bytes" + "encoding/json" + "fmt" + "io" + "net/http" + "github.com/flosch/pongo2" "github.com/gofiber/fiber/v2" + "github.com/stripe/stripe-go" + "github.com/stripe/stripe-go/customer" ) func PricingTableHandler(c *fiber.Ctx) error { @@ -10,9 +18,20 @@ func PricingTableHandler(c *fiber.Ctx) error { } func generatePricingTableChatHTML() string { + var result User + err := edgeClient.QuerySingle(edgeCtx, "SELECT global currentUser { stripe_id, email } LIMIT 1;", &result) + if err != nil { + panic(err) + } + + clientSecretSession := CreateClientSecretSession() + + // TODO Replace by live API call stripeTable := ` - + ` closeBtn := ` @@ -49,12 +68,102 @@ func generatePricingTableChatHTML() string { return htmlString } +func CreateNewStripeCustomer(name string, email string) string { + params := &stripe.CustomerParams{ + Name: stripe.String(name), + Email: stripe.String(email), + } + result, err := customer.New(params) + if err != nil { + panic(err) + } + return result.ID +} + func IsCurrentUserSubscribed() bool { - // TODO Ask Stripe if user is subscribed - return true + var user User + err := edgeClient.QuerySingle(edgeCtx, "SELECT global currentUser { stripe_id } LIMIT 1;", &user) + if err != nil { + panic(err) + } + result, err := customer.Get(user.StripeID, nil) + if err != nil { + panic(err) + } + if result.Subscriptions.TotalCount == 0 { + return false + } + + return result.Subscriptions.Data[0].Plan.Product.ID == "prod_PnDjBBwvQN36cQ" && result.Subscriptions.Data[0].Plan.Active } func IsCurrentUserLimiteReached() bool { - // TODO Ask Stripe if user is subscribed - return true + // Count the number of Usage for the current user + var count int64 + err := edgeClient.QuerySingle(edgeCtx, ` + WITH + U := ( + SELECT Usage + FILTER .user = global currentUser + ) + SELECT count(U) + `, &count) + if err != nil { + panic(err) + } + return count >= 500 +} + +func CreateClientSecretSession() string { + var user User + err := edgeClient.QuerySingle(edgeCtx, "SELECT global currentUser { stripe_id } LIMIT 1;", &user) + if err != nil { + panic(err) + } + + url := "https://api.stripe.com/v1/customer_sessions" + method := "POST" + apiKey := stripe.Key + + payload := []byte(fmt.Sprintf("customer=%s&components[pricing_table][enabled]=true", user.StripeID)) + + req, err := http.NewRequest(method, url, bytes.NewBuffer(payload)) + if err != nil { + panic(fmt.Sprintf("failed to create request: %v", err)) + } + req.Header.Add("Authorization", "Bearer "+apiKey) + req.Header.Add("Content-Type", "application/x-www-form-urlencoded") + + client := &http.Client{} + resp, err := client.Do(req) + if err != nil { + panic(fmt.Sprintf("failed to execute request: %v", err)) + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + body, _ := io.ReadAll(resp.Body) + panic(fmt.Sprintf("failed to create customer session: status code %d, response: %s", resp.StatusCode, string(body))) + } + + body, err := io.ReadAll(resp.Body) + if err != nil { + panic(fmt.Sprintf("failed to read response body: %v", err)) + } + + type CustomerSessionResponse struct { + ClientSecret string `json:"client_secret"` + CustomerID string `json:"customer"` + } + + var customerSession CustomerSessionResponse + if err := json.Unmarshal(body, &customerSession); err != nil { + panic(fmt.Sprintf("failed to unmarshal response: %v", err)) + } + + if user.StripeID != customerSession.CustomerID { + panic(fmt.Sprintf("customer_id does not match: got %s, expected %s", customerSession.CustomerID, user.StripeID)) + } + + return customerSession.ClientSecret } diff --git a/database.go b/database.go index ff270a3..b4ea765 100644 --- a/database.go +++ b/database.go @@ -16,8 +16,12 @@ type Identity struct { } type User struct { - ID edgedb.UUID `edgedb:"id"` - Setting Setting `edgedb:"setting"` + ID edgedb.UUID `edgedb:"id"` + Setting Setting `edgedb:"setting"` + StripeID string `edgedb:"stripe_id"` + Email string `edgedb:"email"` + Name string `edgedb:"name"` + Avatar string `edgedb:"avatar"` } type Key struct { diff --git a/dbschema/default.esdl b/dbschema/default.esdl index 8801852..d9e08fc 100644 --- a/dbschema/default.esdl +++ b/dbschema/default.esdl @@ -10,6 +10,10 @@ module default { type User { required setting: Setting; + required stripe_id: str; + required name: str; + required email: str; + required avatar: str; required identity: ext::auth::Identity { on source delete delete target; } @@ -34,7 +38,7 @@ module default { type Conversation { required name: str; required user: User { - on target delete allow; + on target delete delete source; }; required date: datetime { default := datetime_current(); @@ -66,7 +70,9 @@ module default { type Usage { required model_id: str; - user: User; + user: User { + on target delete allow + }; required input_cost: float32; required output_cost: float32; required input_token: int32; @@ -84,10 +90,10 @@ module default { default := false; }; required modelInfo: ModelInfo { - on target delete allow; + on target delete delete source; }; required user: User { - on target delete allow; + on target delete delete source; }; custom_endpoint: CustomEndpoint { on source delete delete target; diff --git a/dbschema/migrations/00036-m1dn7we.edgeql b/dbschema/migrations/00036-m1dn7we.edgeql new file mode 100644 index 0000000..3675a57 --- /dev/null +++ b/dbschema/migrations/00036-m1dn7we.edgeql @@ -0,0 +1,9 @@ +CREATE MIGRATION m1dn7we4iusvjor3e2axkxlngw22cbxpmqairv6zyo2ufykzrpo6da + ONTO m1e72ubyamp6762oufm57qqns7tkr4o25gc3l62iml7sltlytgx5lq +{ + ALTER TYPE default::Usage { + ALTER LINK user { + ON TARGET DELETE ALLOW; + }; + }; +}; diff --git a/dbschema/migrations/00037-m13buly.edgeql b/dbschema/migrations/00037-m13buly.edgeql new file mode 100644 index 0000000..72106ab --- /dev/null +++ b/dbschema/migrations/00037-m13buly.edgeql @@ -0,0 +1,9 @@ +CREATE MIGRATION m13bulyyzk4lgnxam33lxxez5etrcgyzn5ndnoyrylab2bmjw6jb7q + ONTO m1dn7we4iusvjor3e2axkxlngw22cbxpmqairv6zyo2ufykzrpo6da +{ + ALTER TYPE default::Conversation { + ALTER LINK user { + ON TARGET DELETE DELETE SOURCE; + }; + }; +}; diff --git a/dbschema/migrations/00038-m1qbhq4.edgeql b/dbschema/migrations/00038-m1qbhq4.edgeql new file mode 100644 index 0000000..3f11355 --- /dev/null +++ b/dbschema/migrations/00038-m1qbhq4.edgeql @@ -0,0 +1,12 @@ +CREATE MIGRATION m1qbhq4cgpt5abjvffrrlwaldgec4bfmlai5izca3exry64rofflua + ONTO m13bulyyzk4lgnxam33lxxez5etrcgyzn5ndnoyrylab2bmjw6jb7q +{ + ALTER TYPE default::LLM { + ALTER LINK modelInfo { + ON TARGET DELETE DELETE SOURCE; + }; + ALTER LINK user { + ON TARGET DELETE DELETE SOURCE; + }; + }; +}; diff --git a/dbschema/migrations/00039-m1oinxw.edgeql b/dbschema/migrations/00039-m1oinxw.edgeql new file mode 100644 index 0000000..814fc87 --- /dev/null +++ b/dbschema/migrations/00039-m1oinxw.edgeql @@ -0,0 +1,9 @@ +CREATE MIGRATION m1oinxw74yh5kzyh63mbn2m3kdqpoapkeyo7jvhkp2gq5rjkkvdi3q + ONTO m1qbhq4cgpt5abjvffrrlwaldgec4bfmlai5izca3exry64rofflua +{ + ALTER TYPE default::User { + CREATE REQUIRED PROPERTY stripe_id: std::str { + SET REQUIRED USING ({'Hello'}); + }; + }; +}; diff --git a/dbschema/migrations/00040-m1wlzna.edgeql b/dbschema/migrations/00040-m1wlzna.edgeql new file mode 100644 index 0000000..86c77bb --- /dev/null +++ b/dbschema/migrations/00040-m1wlzna.edgeql @@ -0,0 +1,15 @@ +CREATE MIGRATION m1wlzna3nrrigx2urda3turnj6dcvcjgexeuxxqundupqet5jrkh7q + ONTO m1oinxw74yh5kzyh63mbn2m3kdqpoapkeyo7jvhkp2gq5rjkkvdi3q +{ + ALTER TYPE default::User { + CREATE REQUIRED PROPERTY avatar: std::str { + SET REQUIRED USING ({''}); + }; + CREATE REQUIRED PROPERTY email: std::str { + SET REQUIRED USING ({''}); + }; + CREATE REQUIRED PROPERTY name: std::str { + SET REQUIRED USING ({''}); + }; + }; +}; diff --git a/go.mod b/go.mod index b407a35..3e04eb0 100644 --- a/go.mod +++ b/go.mod @@ -2,71 +2,41 @@ module github.com/MrBounty/JADE2.0 go 1.22.2 +require ( + github.com/edgedb/edgedb-go v0.17.1 + github.com/flosch/pongo2 v0.0.0-20200913210552-0d938eb266f3 + github.com/gofiber/fiber/v2 v2.52.4 + github.com/gofiber/template/django/v3 v3.1.11 + github.com/stripe/stripe-go v70.15.0+incompatible + github.com/yuin/goldmark v1.7.1 + github.com/yuin/goldmark-highlighting v0.0.0-20220208100518-594be1970594 +) + require ( github.com/alecthomas/chroma v0.10.0 // indirect github.com/andybalholm/brotli v1.0.5 // indirect - github.com/bytedance/sonic v1.11.5 // indirect - github.com/bytedance/sonic/loader v0.1.1 // indirect github.com/certifi/gocertifi v0.0.0-20210507211836-431795d63e8d // indirect - github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect - github.com/cloudwego/base64x v0.1.3 // indirect - github.com/cloudwego/iasm v0.2.0 // indirect github.com/dlclark/regexp2 v1.4.0 // indirect - github.com/edgedb/edgedb-go v0.17.1 // indirect - github.com/flosch/pongo2 v0.0.0-20200913210552-0d938eb266f3 // indirect github.com/flosch/pongo2/v6 v6.0.0 // indirect - github.com/gabriel-vasile/mimetype v1.4.3 // indirect - github.com/gin-contrib/sse v0.1.0 // indirect - github.com/gin-gonic/gin v1.9.1 // indirect - github.com/go-playground/locales v0.14.1 // indirect - github.com/go-playground/universal-translator v0.18.1 // indirect - github.com/go-playground/validator/v10 v10.19.0 // indirect - github.com/goccy/go-json v0.10.2 // indirect - github.com/gofiber/fiber/v2 v2.52.4 // indirect github.com/gofiber/template v1.8.3 // indirect - github.com/gofiber/template/django/v3 v3.1.11 // indirect - github.com/gofiber/template/html/v2 v2.1.1 // indirect github.com/gofiber/utils v1.1.0 // indirect - github.com/golang/snappy v0.0.1 // indirect - github.com/gomarkdown/markdown v0.0.0-20240419095408-642f0ee99ae2 // indirect github.com/google/uuid v1.5.0 // indirect - github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/compress v1.17.0 // indirect - github.com/klauspost/cpuid/v2 v2.2.7 // indirect - github.com/leodido/go-urn v1.4.0 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-runewidth v0.0.15 // indirect - github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect - github.com/modern-go/reflect2 v1.0.2 // indirect - github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe // indirect - github.com/pelletier/go-toml/v2 v2.2.1 // indirect github.com/rivo/uniseg v0.2.0 // indirect github.com/sigurn/crc16 v0.0.0-20211026045750-20ab5afb07e3 // indirect - github.com/stripe/stripe-go/v78 v78.7.0 // indirect - github.com/twitchyliquid64/golang-asm v0.15.1 // indirect - github.com/ugorji/go/codec v1.2.12 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/fasthttp v1.51.0 // indirect github.com/valyala/tcplisten v1.0.0 // indirect - github.com/xdg-go/pbkdf2 v1.0.0 // indirect - github.com/xdg-go/scram v1.1.2 // indirect - github.com/xdg-go/stringprep v1.0.4 // indirect github.com/xdg/scram v1.0.5 // indirect github.com/xdg/stringprep v1.0.3 // indirect - github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect - github.com/yuin/goldmark v1.7.1 // indirect - github.com/yuin/goldmark-highlighting v0.0.0-20220208100518-594be1970594 // indirect - go.mongodb.org/mongo-driver v1.15.0 // indirect - golang.org/x/arch v0.7.0 // indirect golang.org/x/crypto v0.22.0 // indirect golang.org/x/exp v0.0.0-20230510235704-dd950f8aeaea // indirect golang.org/x/mod v0.10.0 // indirect golang.org/x/net v0.24.0 // indirect - golang.org/x/sync v0.2.0 // indirect golang.org/x/sys v0.19.0 // indirect golang.org/x/text v0.14.0 // indirect golang.org/x/tools v0.9.3 // indirect - google.golang.org/protobuf v1.33.0 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index b7f06c2..8c738e7 100644 --- a/go.sum +++ b/go.sum @@ -2,25 +2,10 @@ github.com/alecthomas/chroma v0.10.0 h1:7XDcGkCQopCNKjZHfYrNLraA+M7e0fMiJ/Mfikbf github.com/alecthomas/chroma v0.10.0/go.mod h1:jtJATyUxlIORhUOFNA9NZDWGAQ8wpxQQqNSB4rjA/1s= github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs= github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= -github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= -github.com/bytedance/sonic v1.10.0-rc/go.mod h1:ElCzW+ufi8qKqNW0FY314xriJhyJhuoJ3gFZdAHF7NM= -github.com/bytedance/sonic v1.11.5 h1:G00FYjjqll5iQ1PYXynbg/hyzqBqavH8Mo9/oTopd9k= -github.com/bytedance/sonic v1.11.5/go.mod h1:X2PC2giUdj/Cv2lliWFLk6c/DUQok5rViJSemeB0wDw= -github.com/bytedance/sonic/loader v0.1.0/go.mod h1:UmRT+IRTGKz/DAkzcEGzyVqQFJ7H9BqwBO3pm9H/+HY= -github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM= -github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= github.com/certifi/gocertifi v0.0.0-20210507211836-431795d63e8d h1:S2NE3iHSwP0XV47EEXL8mWmRdEfGscSJ+7EgePNgt0s= github.com/certifi/gocertifi v0.0.0-20210507211836-431795d63e8d/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA= -github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= -github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= -github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d h1:77cEq6EriyTZ0g/qfRdp61a3Uu/AWrgIq2s0ClJV1g0= -github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d/go.mod h1:8EPpVsBuRksnlj1mLy4AWzRNQYxauNi62uWcE3to6eA= -github.com/chenzhuoyu/iasm v0.9.0/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog= -github.com/cloudwego/base64x v0.1.3 h1:b5J/l8xolB7dyDTTmhJP2oTs5LdrjyrUFuNxdfq5hAg= -github.com/cloudwego/base64x v0.1.3/go.mod h1:1+1K5BUHIQzyapgpF7LwvOGAEDicKtt1umPV+aN8pi8= -github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg= -github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dlclark/regexp2 v1.4.0 h1:F1rxgk7p4uKjwIQxBs9oAXe5CqrXlCduYEJvrF4u93E= github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= @@ -30,51 +15,24 @@ github.com/flosch/pongo2 v0.0.0-20200913210552-0d938eb266f3 h1:fmFk0Wt3bBxxwZnu4 github.com/flosch/pongo2 v0.0.0-20200913210552-0d938eb266f3/go.mod h1:bJWSKrZyQvfTnb2OudyUjurSG4/edverV7n82+K3JiM= github.com/flosch/pongo2/v6 v6.0.0 h1:lsGru8IAzHgIAw6H2m4PCyleO58I40ow6apih0WprMU= github.com/flosch/pongo2/v6 v6.0.0/go.mod h1:CuDpFm47R0uGGE7z13/tTlt1Y6zdxvr2RLT5LJhsHEU= -github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0= -github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk= -github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= -github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= -github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= -github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU= -github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= -github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= -github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= -github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= -github.com/go-playground/validator/v10 v10.19.0 h1:ol+5Fu+cSq9JD7SoSqe04GMI92cbn0+wvQ3bZ8b/AU4= -github.com/go-playground/validator/v10 v10.19.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= -github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= -github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/gofiber/fiber/v2 v2.52.4 h1:P+T+4iK7VaqUsq2PALYEfBBo6bJZ4q3FP8cZ84EggTM= github.com/gofiber/fiber/v2 v2.52.4/go.mod h1:KEOE+cXMhXG0zHc9d8+E38hoX+ZN7bhOtgeF2oT6jrQ= github.com/gofiber/template v1.8.3 h1:hzHdvMwMo/T2kouz2pPCA0zGiLCeMnoGsQZBTSYgZxc= github.com/gofiber/template v1.8.3/go.mod h1:bs/2n0pSNPOkRa5VJ8zTIvedcI/lEYxzV3+YPXdBvq8= github.com/gofiber/template/django/v3 v3.1.11 h1:wE5k/wWNKGKxfeopaeB6IBijMiEVAxKHJVf1WMH5iNw= github.com/gofiber/template/django/v3 v3.1.11/go.mod h1:sEUp0cr1iCuFx4GEtHEA7yRXgJmRdAVXwGMR3Q5JnyI= -github.com/gofiber/template/html/v2 v2.1.1 h1:QEy3O3EBkvwDthy5bXVGUseOyO6ldJoiDxlF4+MJiV8= -github.com/gofiber/template/html/v2 v2.1.1/go.mod h1:2G0GHHOUx70C1LDncoBpe4T6maQbNa4x1CVNFW0wju0= github.com/gofiber/utils v1.1.0 h1:vdEBpn7AzIUJRhe+CiTOJdUcTg4Q9RK+pEa0KPbLdrM= github.com/gofiber/utils v1.1.0/go.mod h1:poZpsnhBykfnY1Mc0KeEa6mSHrS3dV0+oBWyeQmb2e0= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4= -github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/gomarkdown/markdown v0.0.0-20240419095408-642f0ee99ae2 h1:yEt5djSYb4iNtmV9iJGVday+i4e9u6Mrn5iP64HH5QM= -github.com/gomarkdown/markdown v0.0.0-20240419095408-642f0ee99ae2/go.mod h1:JDGcbDT52eL4fju3sZ4TeHGsQwhG9nbDV21aMyhwPoA= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU= github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= -github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/klauspost/compress v1.17.0 h1:Rnbp4K9EjcDuVuHtd0dgA4qNuv9yKDYKK1ulpJwgrqM= github.com/klauspost/compress v1.17.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= -github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= -github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM= -github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= -github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= +github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= 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/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= -github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 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.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= @@ -82,121 +40,56 @@ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWE github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= -github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= -github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe h1:iruDEfMl2E6fbMZ9s0scYfZQ84/6SPL6zC8ACM2oIL0= -github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -github.com/pelletier/go-toml/v2 v2.2.1 h1:9TA9+T8+8CUCO2+WYnDLCgrYi9+omqKXyjDtosvtEhg= -github.com/pelletier/go-toml/v2 v2.2.1/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= +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/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/sigurn/crc16 v0.0.0-20211026045750-20ab5afb07e3 h1:aQKxg3+2p+IFXXg97McgDGT5zcMrQoi0EICZs8Pgchs= github.com/sigurn/crc16 v0.0.0-20211026045750-20ab5afb07e3/go.mod h1:9/etS5gpQq9BJsJMWg1wpLbfuSnkm8dPF6FdW2JXVhA= github.com/stretchr/objx v0.1.0/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/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 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.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -github.com/stripe/stripe-go/v78 v78.7.0 h1:TdTkzBn0wB0ntgOI74YHpvsNyHPBijX83n4ljsjXh6o= -github.com/stripe/stripe-go/v78 v78.7.0/go.mod h1:GjncxVLUc1xoIOidFqVwq+y3pYiG7JLVWiVQxTsLrvQ= -github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= -github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= -github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= -github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= +github.com/stripe/stripe-go v70.15.0+incompatible h1:hNML7M1zx8RgtepEMlxyu/FpVPrP7KZm1gPFQquJQvM= +github.com/stripe/stripe-go v70.15.0+incompatible/go.mod h1:A1dQZmO/QypXmsL0T8axYZkSN/uA/T/A64pfKdBAMiY= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fasthttp v1.51.0 h1:8b30A5JlZ6C7AS81RsWjYMQmrZG6feChmgAolCl1SqA= github.com/valyala/fasthttp v1.51.0/go.mod h1:oI2XroL+lI7vdXyYoQk03bXBThfFl2cVdIA3Xl7cH8g= github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8= github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= -github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c= -github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= -github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY= -github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4= -github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8= -github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM= github.com/xdg/scram v1.0.5 h1:TuS0RFmt5Is5qm9Tm2SoD89OPqe4IRiFtyFY4iwWXsw= github.com/xdg/scram v1.0.5/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I= github.com/xdg/stringprep v1.0.3 h1:cmL5Enob4W83ti/ZHuZLuKD/xqJfus4fVPwE+/BDm+4= github.com/xdg/stringprep v1.0.3/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y= -github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d h1:splanxYIlg+5LfHAM6xpdFEAYOk8iySO56hMFq6uLyA= -github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= github.com/yuin/goldmark v1.4.5/go.mod h1:rmuwmfZ0+bvzB24eSC//bk1R1Zp3hM0OXYv/G2LIilg= -github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yuin/goldmark v1.7.1 h1:3bajkSilaCbjdKVsKdZjZCLBNPL9pYzrCakKaf4U49U= github.com/yuin/goldmark v1.7.1/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E= github.com/yuin/goldmark-highlighting v0.0.0-20220208100518-594be1970594 h1:yHfZyN55+5dp1wG7wDKv8HQ044moxkyGq12KFFMFDxg= github.com/yuin/goldmark-highlighting v0.0.0-20220208100518-594be1970594/go.mod h1:U9ihbh+1ZN7fR5Se3daSPoz1CGF9IYtSvWwVQtnzGHU= -go.mongodb.org/mongo-driver v1.15.0 h1:rJCKC8eEliewXjZGf0ddURtl7tTVy1TK3bfl0gkUSLc= -go.mongodb.org/mongo-driver v1.15.0/go.mod h1:Vzb0Mk/pa7e6cWw85R4F/endUC3u0U9jGcNU603k65c= -golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= -golang.org/x/arch v0.7.0 h1:pskyeJh/3AmoQ8CPE95vxHLqp1G1GfGNXTmcl9NEKTc= -golang.org/x/arch v0.7.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30= golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= golang.org/x/exp v0.0.0-20230510235704-dd950f8aeaea h1:vLCWI/yYrdEHyN2JzIzPO3aaQJHQdp89IZBA/+azVC4= golang.org/x/exp v0.0.0-20230510235704-dd950f8aeaea/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk= golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210520170846-37e1c6afe023/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= -golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI= golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/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= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.9.3 h1:Gn1I8+64MsuTb/HpH+LmQtNas23LhUVr3rYZ0eKuaMM= golang.org/x/tools v0.9.3/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= -google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= -rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= diff --git a/login.go b/login.go index 81d7ae0..f1ab1b5 100644 --- a/login.go +++ b/login.go @@ -7,7 +7,6 @@ import ( "encoding/json" "fmt" "io" - "io/ioutil" "net/http" "github.com/edgedb/edgedb-go" @@ -19,11 +18,21 @@ type DiscoveryDocument struct { } type UserProfile struct { - Email string `json:"email"` - Name string `json:"name"` + Email string `json:"email"` + Name string `json:"name"` + AvatarGitHub string `json:"avatar_url"` + AvatarGoogle string `json:"picture"` } -func getGoogleUserProfile(providerToken string) (string, string) { +type TokenResponse struct { + AuthToken string `json:"auth_token"` + IdentityID string `json:"identity_id"` + ProviderToken string `json:"provider_token"` +} + +const EDGEDB_AUTH_BASE_URL = "http://127.0.0.1:10700/db/main/ext/auth" + +func getGoogleUserProfile(providerToken string) (string, string, string) { // Fetch the discovery document resp, err := http.Get("https://accounts.google.com/.well-known/openid-configuration") if err != nil { @@ -35,7 +44,7 @@ func getGoogleUserProfile(providerToken string) (string, string) { panic(fmt.Sprintf("failed to fetch discovery document: status code %d", resp.StatusCode)) } - body, err := ioutil.ReadAll(resp.Body) + body, err := io.ReadAll(resp.Body) if err != nil { panic(fmt.Sprintf("failed to read discovery document response: %v", err)) } @@ -64,7 +73,7 @@ func getGoogleUserProfile(providerToken string) (string, string) { panic(fmt.Sprintf("failed to fetch user profile: status code %d", resp.StatusCode)) } - body, err = ioutil.ReadAll(resp.Body) + body, err = io.ReadAll(resp.Body) if err != nil { panic(fmt.Sprintf("failed to read user profile response: %v", err)) } @@ -74,10 +83,10 @@ func getGoogleUserProfile(providerToken string) (string, string) { panic(fmt.Sprintf("failed to unmarshal user profile: %v", err)) } - return userProfile.Email, userProfile.Name + return userProfile.Email, userProfile.Name, userProfile.AvatarGoogle } -func getGitHubUserProfile(providerToken string) (string, string) { +func getGitHubUserProfile(providerToken string) (string, string, string) { // Create the request to fetch the user profile req, err := http.NewRequest("GET", "https://api.github.com/user", nil) if err != nil { @@ -98,21 +107,19 @@ func getGitHubUserProfile(providerToken string) (string, string) { panic(fmt.Sprintf("failed to fetch user profile: status code %d", resp.StatusCode)) } - body, err := ioutil.ReadAll(resp.Body) + body, err := io.ReadAll(resp.Body) if err != nil { panic(fmt.Sprintf("failed to read user profile response: %v", err)) } - var userProfile GitHubUserProfile + var userProfile UserProfile if err := json.Unmarshal(body, &userProfile); err != nil { panic(fmt.Sprintf("failed to unmarshal user profile: %v", err)) } - return userProfile.Email, userProfile.Name + return userProfile.Email, userProfile.Name, userProfile.AvatarGitHub } -const EDGEDB_AUTH_BASE_URL = "http://127.0.0.1:10700/db/main/ext/auth" - func generatePKCE() (string, string) { verifier_source := make([]byte, 32) _, err := rand.Read(verifier_source) @@ -164,11 +171,7 @@ func handleCallbackSignup(c *fiber.Ctx) error { return c.Status(fiber.StatusBadRequest).SendString(fmt.Sprintf("Error from the auth server: %s", string(body))) } - var tokenResponse struct { - AuthToken string `json:"auth_token"` - IdentityID string `json:"identity_id"` - ProviderToken string `json:"provider_token"` - } + var tokenResponse TokenResponse err = json.NewDecoder(resp.Body).Decode(&tokenResponse) if err != nil { return c.Status(fiber.StatusBadRequest).SendString(fmt.Sprintf("Error decoding auth server response: %s", err.Error())) @@ -199,43 +202,34 @@ func handleCallbackSignup(c *fiber.Ctx) error { } var ( - providerEmail string - providerName string + providerEmail string + providerName string + providerAvatar string ) // Get the email and name from the provider if identity.Issuer == "https://accounts.google.com" { - providerEmail, providerName = getGoogleUserProfile(tokenResponse.ProviderToken) + providerEmail, providerName, providerAvatar = getGoogleUserProfile(tokenResponse.ProviderToken) } else if identity.Issuer == "https://github.com" { - providerEmail, providerName = getGithubUserProfile(tokenResponse.ProviderToken) + providerEmail, providerName, providerAvatar = getGitHubUserProfile(tokenResponse.ProviderToken) // Work !!!! } - fmt.Println(providerEmail, providerName) - - // Create stripe user - //stripe.Key = "sk_test_51OxXuWP2nW0okNQyiNAOcBTTWZSiyP1el5KOmV3yIv1DQR0415YPsH1eb89SLrsOFj80o9p2AxGOy042e53yDvZN00jHxHAbE6" - // - //params := &stripe.CustomerParams{ - // Name: stripe.String( - // Email: stripe.String(), - //} - //result, err := customer.New(params) - - // Create a new User and attach the identity - if err != nil { - return c.Status(fiber.StatusInternalServerError).SendString(fmt.Sprintf("Error in edgedb.ParseUUID: in handleCallbackSignup: %s", err.Error())) - } + stripCustID := CreateNewStripeCustomer(providerName, providerEmail) err = edgeClient.Execute(edgeCtx, ` INSERT User { + stripe_id := $0, + email := $1, + name := $2, + avatar := $3, setting := ( INSERT Setting { default_model := "gpt-3.5-turbo" } ), - identity := (SELECT ext::auth::Identity FILTER .id = $0) + identity := (SELECT ext::auth::Identity FILTER .id = $4) } - `, identityUUID) + `, stripCustID, providerEmail, providerName, providerAvatar, identityUUID) if err != nil { return c.Status(fiber.StatusInternalServerError).SendString(fmt.Sprintf("Error in edgedb.QuerySingle: in handleCallbackSignup: %s", err.Error())) } @@ -269,9 +263,7 @@ func handleCallback(c *fiber.Ctx) error { return c.Status(fiber.StatusBadRequest).SendString(fmt.Sprintf("Error from the auth server: %s", string(body))) } - var tokenResponse struct { - AuthToken string `json:"auth_token"` - } + var tokenResponse TokenResponse err = json.NewDecoder(resp.Body).Decode(&tokenResponse) if err != nil { return c.Status(fiber.StatusBadRequest).SendString(fmt.Sprintf("Error decoding auth server response: %s", err.Error())) diff --git a/main.go b/main.go index b3d67ed..d1b4572 100644 --- a/main.go +++ b/main.go @@ -11,6 +11,7 @@ import ( "github.com/gofiber/fiber/v2/middleware/logger" "github.com/gofiber/fiber/v2/middleware/recover" "github.com/gofiber/template/django/v3" + "github.com/stripe/stripe-go" ) var userTmpl *pongo2.Template @@ -40,6 +41,9 @@ func sendEvent(event, data string) { } func main() { + // TODO Change key to env and the live one + stripe.Key = "sk_test_51OxXuWP2nW0okNQyiNAOcBTTWZSiyP1el5KOmV3yIv1DQR0415YPsH1eb89SLrsOFj80o9p2AxGOy042e53yDvZN00jHxHAbE6" + botTmpl = pongo2.Must(pongo2.FromFile("views/partials/message-bot.html")) userTmpl = pongo2.Must(pongo2.FromFile("views/partials/message-user.html")) selectBtnTmpl = pongo2.Must(pongo2.FromFile("views/partials/model-selection-btn.html")) diff --git a/views/partials/popover-settings.html b/views/partials/popover-settings.html index 1f82e55..b528e29 100644 --- a/views/partials/popover-settings.html +++ b/views/partials/popover-settings.html @@ -93,7 +93,7 @@

{% if IsSub %} - +