Working payment with stripe

This commit is contained in:
Adrien Bouvais 2024-05-24 15:11:06 +02:00
parent 106dac39e6
commit 435149ad5e
14 changed files with 293 additions and 217 deletions

52
Chat.go
View File

@ -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 := `
<stripe-pricing-table pricing-table-id="prctbl_1PJAxDP2nW0okNQyY0Q3mbg4"
publishable-key="pk_live_51OxXuWP2nW0okNQyme1qdwbL535jbMmM1uIUi6U5zcvEUUwKraktmpCzudXNdPSTxlHpw2FbCtxpwbyFFcasQ7aj000tJJGpWW">
<stripe-pricing-table
pricing-table-id="prctbl_1OxrazP2nW0okNQymYvskUk7"
publishable-key="pk_test_51OxXuWP2nW0okNQy2jyS70vx7WHZzDskvQazsitSDJQ3ifVHPqAkMv7orCePwRGRTarNn8uMuaxbVqD2Zg80oRc600epN4ycQ4"
customer-session-client-secret="` + clientSecretSession + `">
</stripe-pricing-table>`
htmlString := "<div class='columns is-centered' id='chat-container'><div class='column is-12-mobile is-8-tablet is-6-desktop' id='chat-messages'>"
@ -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)

121
Stripe.go
View File

@ -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 := `
<stripe-pricing-table pricing-table-id="prctbl_1PJAxDP2nW0okNQyY0Q3mbg4"
publishable-key="pk_live_51OxXuWP2nW0okNQyme1qdwbL535jbMmM1uIUi6U5zcvEUUwKraktmpCzudXNdPSTxlHpw2FbCtxpwbyFFcasQ7aj000tJJGpWW">
<stripe-pricing-table
pricing-table-id="prctbl_1OxrazP2nW0okNQymYvskUk7"
publishable-key="pk_test_51OxXuWP2nW0okNQy2jyS70vx7WHZzDskvQazsitSDJQ3ifVHPqAkMv7orCePwRGRTarNn8uMuaxbVqD2Zg80oRc600epN4ycQ4"
customer-session-client-secret="` + clientSecretSession + `">
</stripe-pricing-table>`
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
}

View File

@ -18,6 +18,10 @@ type Identity struct {
type User struct {
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 {

View File

@ -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;

View File

@ -0,0 +1,9 @@
CREATE MIGRATION m1dn7we4iusvjor3e2axkxlngw22cbxpmqairv6zyo2ufykzrpo6da
ONTO m1e72ubyamp6762oufm57qqns7tkr4o25gc3l62iml7sltlytgx5lq
{
ALTER TYPE default::Usage {
ALTER LINK user {
ON TARGET DELETE ALLOW;
};
};
};

View File

@ -0,0 +1,9 @@
CREATE MIGRATION m13bulyyzk4lgnxam33lxxez5etrcgyzn5ndnoyrylab2bmjw6jb7q
ONTO m1dn7we4iusvjor3e2axkxlngw22cbxpmqairv6zyo2ufykzrpo6da
{
ALTER TYPE default::Conversation {
ALTER LINK user {
ON TARGET DELETE DELETE SOURCE;
};
};
};

View File

@ -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;
};
};
};

View File

@ -0,0 +1,9 @@
CREATE MIGRATION m1oinxw74yh5kzyh63mbn2m3kdqpoapkeyo7jvhkp2gq5rjkkvdi3q
ONTO m1qbhq4cgpt5abjvffrrlwaldgec4bfmlai5izca3exry64rofflua
{
ALTER TYPE default::User {
CREATE REQUIRED PROPERTY stripe_id: std::str {
SET REQUIRED USING (<std::str>{'Hello'});
};
};
};

View File

@ -0,0 +1,15 @@
CREATE MIGRATION m1wlzna3nrrigx2urda3turnj6dcvcjgexeuxxqundupqet5jrkh7q
ONTO m1oinxw74yh5kzyh63mbn2m3kdqpoapkeyo7jvhkp2gq5rjkkvdi3q
{
ALTER TYPE default::User {
CREATE REQUIRED PROPERTY avatar: std::str {
SET REQUIRED USING (<std::str>{''});
};
CREATE REQUIRED PROPERTY email: std::str {
SET REQUIRED USING (<std::str>{''});
};
CREATE REQUIRED PROPERTY name: std::str {
SET REQUIRED USING (<std::str>{''});
};
};
};

50
go.mod
View File

@ -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
)

129
go.sum
View File

@ -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=

View File

@ -7,7 +7,6 @@ import (
"encoding/json"
"fmt"
"io"
"io/ioutil"
"net/http"
"github.com/edgedb/edgedb-go"
@ -21,9 +20,19 @@ type DiscoveryDocument struct {
type UserProfile struct {
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()))
@ -201,41 +204,32 @@ func handleCallbackSignup(c *fiber.Ctx) error {
var (
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 := <str>$0,
email := <str>$1,
name := <str>$2,
avatar := <str>$3,
setting := (
INSERT Setting {
default_model := "gpt-3.5-turbo"
}
),
identity := (SELECT ext::auth::Identity FILTER .id = <uuid>$0)
identity := (SELECT ext::auth::Identity FILTER .id = <uuid>$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()))

View File

@ -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"))

View File

@ -93,7 +93,7 @@
<p id="api-keys-status"></p>
</div>
{% if IsSub %}
<a class="button is-small mt-1" href="/subscribe">
<a class="button is-small mt-1" href="{{ StripeSubLink }}" target="_blank">
<span class="icon is-small" style="color: #b00202">
<i class="fa-solid fa-heart"></i>
</span>