This commit is contained in:
Adrien Bouvais 2024-05-24 09:48:33 +02:00
parent 871f8e3a61
commit 106dac39e6
20 changed files with 219 additions and 49 deletions

41
Chat.go
View File

@ -3,7 +3,6 @@ package main
import ( import (
"context" "context"
"encoding/json" "encoding/json"
"fmt"
"sort" "sort"
"strings" "strings"
"time" "time"
@ -20,7 +19,7 @@ func ChatPageHandler(c *fiber.Ctx) error {
edgeClient = edgeClient.WithGlobals(map[string]interface{}{"ext::auth::client_token": authCookie}) edgeClient = edgeClient.WithGlobals(map[string]interface{}{"ext::auth::client_token": authCookie})
} }
return c.Render("chat", fiber.Map{"IsLogin": checkIfLogin(), "HaveKey": checkIfHaveKey()}, "layouts/main") return c.Render("chat", fiber.Map{"IsLogin": checkIfLogin(), "HaveKey": checkIfHaveKey(), "IsSubscribed": IsCurrentUserSubscribed(), "IsLimiteReached": IsCurrentUserLimiteReached()}, "layouts/main")
} }
func DeleteMessageHandler(c *fiber.Ctx) error { func DeleteMessageHandler(c *fiber.Ctx) error {
@ -49,7 +48,9 @@ func LoadChatHandler(c *fiber.Ctx) error {
deleteLLMtoDelete() deleteLLMtoDelete()
if checkIfLogin() { if checkIfLogin() {
if getCurrentUserKeys() == nil { if IsCurrentUserLimiteReached() && !IsCurrentUserSubscribed() {
return c.SendString(generateLimitReachedChatHTML())
} else if getCurrentUserKeys() == nil {
return c.SendString(generateEnterKeyChatHTML()) return c.SendString(generateEnterKeyChatHTML())
} }
return c.SendString(generateChatHTML()) return c.SendString(generateChatHTML())
@ -272,6 +273,38 @@ func generateEnterKeyChatHTML() string {
return htmlString return htmlString
} }
func generateLimitReachedChatHTML() string {
welcomeMessage := `You have reached the maximum number of messages for a free account. Please upgrade your account to continue using JADE.`
stripeTable := `
<stripe-pricing-table pricing-table-id="prctbl_1PJAxDP2nW0okNQyY0Q3mbg4"
publishable-key="pk_live_51OxXuWP2nW0okNQyme1qdwbL535jbMmM1uIUi6U5zcvEUUwKraktmpCzudXNdPSTxlHpw2FbCtxpwbyFFcasQ7aj000tJJGpWW">
</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'>"
NextMessages := []TemplateMessage{}
nextMsg := TemplateMessage{
Icon: "icons/bouvai2.png", // Assuming Icon is a field you want to include from Message
Content: "<br>" + markdownToHTML(welcomeMessage) + "<br>" + stripeTable,
Hidden: false, // Assuming Hidden is a field you want to include from Message
Id: "0",
Name: "JADE",
}
NextMessages = append(NextMessages, nextMsg)
botOut, err := botTmpl.Execute(pongo2.Context{"Messages": NextMessages, "ConversationAreaId": 0, "NotClickable": true})
if err != nil {
panic(err)
}
htmlString += botOut
htmlString += "<div style='height: 10px;'></div>"
htmlString += "</div></div>"
// Render the HTML template with the messages
return htmlString
}
// Buton actions // Buton actions
func GetEditMessageFormHandler(c *fiber.Ctx) error { func GetEditMessageFormHandler(c *fiber.Ctx) error {
id := c.FormValue("id") id := c.FormValue("id")
@ -430,7 +463,6 @@ func LoadUsageKPIHandler(c *fiber.Ctx) error {
} FILTER .total_count > 0 ORDER BY .total_cost DESC } FILTER .total_count > 0 ORDER BY .total_cost DESC
`, &usages, InputDate, InputDate.AddDate(0, 1, 0)) `, &usages, InputDate, InputDate.AddDate(0, 1, 0))
if err != nil { if err != nil {
fmt.Println(err)
panic(err) panic(err)
} }
@ -552,6 +584,7 @@ func LoadSettingsHandler(c *fiber.Ctx) error {
"GooseaiExists": gooseaiExists, "GooseaiExists": gooseaiExists,
"GoogleExists": googleExists, "GoogleExists": googleExists,
"AnyExists": openaiExists || anthropicExists || mistralExists || groqExists || gooseaiExists || googleExists, "AnyExists": openaiExists || anthropicExists || mistralExists || groqExists || gooseaiExists || googleExists,
"IsSub": IsCurrentUserSubscribed(),
}) })
if err != nil { if err != nil {
panic(err) panic(err)

4
LLM.go
View File

@ -2,7 +2,6 @@ package main
import ( import (
"encoding/json" "encoding/json"
"fmt"
"strconv" "strconv"
"github.com/edgedb/edgedb-go" "github.com/edgedb/edgedb-go"
@ -61,8 +60,6 @@ func createLLM(c *fiber.Ctx) error {
token := c.FormValue("model-key-input") token := c.FormValue("model-key-input")
customID := c.FormValue("model-cid-input") customID := c.FormValue("model-cid-input")
fmt.Println(name, modelID, temperatureFloat, systemPrompt, url, token)
if modelID == "custom" { if modelID == "custom" {
err := edgeClient.Execute(edgeCtx, ` err := edgeClient.Execute(edgeCtx, `
INSERT LLM { INSERT LLM {
@ -85,7 +82,6 @@ func createLLM(c *fiber.Ctx) error {
}; };
`, name, systemPrompt, temperatureFloat, url, token, customID) // TODO Add real max token `, name, systemPrompt, temperatureFloat, url, token, customID) // TODO Add real max token
if err != nil { if err != nil {
fmt.Println("Error in createLLM: ", err)
panic(err) panic(err)
} }
} else { } else {

View File

@ -153,7 +153,6 @@ func GenerateMultipleMessagesHandler(c *fiber.Ctx) error {
FILTER .id = <uuid>$0; FILTER .id = <uuid>$0;
`, &message, messageID) `, &message, messageID)
if err != nil { if err != nil {
fmt.Println("Is it here ?")
panic(err) panic(err)
} }

View File

@ -103,8 +103,6 @@ func TestGoogleKey(apiKey string) bool {
return false return false
} }
fmt.Println(string(body))
var chatCompletionResponse GoogleChatCompletionResponse var chatCompletionResponse GoogleChatCompletionResponse
err = json.Unmarshal(body, &chatCompletionResponse) err = json.Unmarshal(body, &chatCompletionResponse)
if err != nil { if err != nil {

View File

@ -82,9 +82,6 @@ func TestGooseaiKey(apiKey string) bool {
return false return false
} }
// Print the response body
fmt.Println(string(body))
var chatCompletionResponse GooseaiCompletionResponse var chatCompletionResponse GooseaiCompletionResponse
err = json.Unmarshal(body, &chatCompletionResponse) err = json.Unmarshal(body, &chatCompletionResponse)
if err != nil { if err != nil {

View File

@ -77,8 +77,6 @@ func RequestHuggingface(llm LLM, messages []Message, temperature float64) (Huggi
req.Header.Set("Authorization", "Bearer "+llm.Endpoint.Key) req.Header.Set("Authorization", "Bearer "+llm.Endpoint.Key)
req.Header.Set("Content-Type", "application/json") req.Header.Set("Content-Type", "application/json")
fmt.Println(url, llm.Endpoint.Key)
client := &http.Client{} client := &http.Client{}
resp, err := client.Do(req) resp, err := client.Do(req)
if err != nil { if err != nil {

View File

@ -43,7 +43,6 @@ func addMistralMessage(llm LLM, selected bool) edgedb.UUID {
if err != nil { if err != nil {
panic(err) panic(err)
} else if len(chatCompletion.Choices) == 0 { } else if len(chatCompletion.Choices) == 0 {
fmt.Println("No response from Mistral")
id := insertBotMessage("No response from Mistral", selected, llm.ID) id := insertBotMessage("No response from Mistral", selected, llm.ID)
return id return id
} else { } else {
@ -72,13 +71,11 @@ func TestMistralKey(apiKey string) bool {
jsonBody, err := json.Marshal(requestBody) jsonBody, err := json.Marshal(requestBody)
if err != nil { if err != nil {
fmt.Println("Error:", err)
return false return false
} }
req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonBody)) req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonBody))
if err != nil { if err != nil {
fmt.Println("Error:", err)
return false return false
} }
@ -89,25 +86,21 @@ func TestMistralKey(apiKey string) bool {
client := &http.Client{} client := &http.Client{}
resp, err := client.Do(req) resp, err := client.Do(req)
if err != nil { if err != nil {
fmt.Println("Error:", err)
return false return false
} }
defer resp.Body.Close() defer resp.Body.Close()
body, err := io.ReadAll(resp.Body) body, err := io.ReadAll(resp.Body)
if err != nil { if err != nil {
fmt.Println("Error:", err)
return false return false
} }
var chatCompletionResponse MistralChatCompletionResponse var chatCompletionResponse MistralChatCompletionResponse
err = json.Unmarshal(body, &chatCompletionResponse) err = json.Unmarshal(body, &chatCompletionResponse)
if err != nil { if err != nil {
fmt.Println("Error:", err)
return false return false
} }
if chatCompletionResponse.Usage.CompletionTokens == 0 { if chatCompletionResponse.Usage.CompletionTokens == 0 {
fmt.Println("Error: No response from Mistral")
return false return false
} }
return true return true

View File

@ -124,10 +124,6 @@ func RequestOpenai(model string, messages []Message, temperature float64, contex
url := "https://api.openai.com/v1/chat/completions" url := "https://api.openai.com/v1/chat/completions"
fmt.Println(context)
fmt.Println(Message2RequestMessage(messages, context))
requestBody := OpenaiChatCompletionRequest{ requestBody := OpenaiChatCompletionRequest{
Model: model, Model: model,
Messages: Message2RequestMessage(messages, context), Messages: Message2RequestMessage(messages, context),

View File

@ -51,5 +51,10 @@ func generatePricingTableChatHTML() string {
func IsCurrentUserSubscribed() bool { func IsCurrentUserSubscribed() bool {
// TODO Ask Stripe if user is subscribed // TODO Ask Stripe if user is subscribed
return false return true
}
func IsCurrentUserLimiteReached() bool {
// TODO Ask Stripe if user is subscribed
return true
} }

View File

@ -10,6 +10,11 @@ import (
var edgeCtx context.Context var edgeCtx context.Context
var edgeClient *edgedb.Client var edgeClient *edgedb.Client
type Identity struct {
ID edgedb.UUID `edgedb:"id"`
Issuer string `edgedb:"issuer"`
}
type User struct { type User struct {
ID edgedb.UUID `edgedb:"id"` ID edgedb.UUID `edgedb:"id"`
Setting Setting `edgedb:"setting"` Setting Setting `edgedb:"setting"`

View File

@ -10,7 +10,9 @@ module default {
type User { type User {
required setting: Setting; required setting: Setting;
required identity: ext::auth::Identity; required identity: ext::auth::Identity {
on source delete delete target;
}
} }
type Key { type Key {

View File

@ -0,0 +1,9 @@
CREATE MIGRATION m1e72ubyamp6762oufm57qqns7tkr4o25gc3l62iml7sltlytgx5lq
ONTO m1x75hdgm27pmshypxbzfrhje6xru5ypx65efdiu6zuwnute2xschq
{
ALTER TYPE default::User {
ALTER LINK identity {
ON SOURCE DELETE DELETE TARGET;
};
};
};

1
go.mod
View File

@ -43,6 +43,7 @@ require (
github.com/pelletier/go-toml/v2 v2.2.1 // indirect github.com/pelletier/go-toml/v2 v2.2.1 // indirect
github.com/rivo/uniseg v0.2.0 // indirect github.com/rivo/uniseg v0.2.0 // indirect
github.com/sigurn/crc16 v0.0.0-20211026045750-20ab5afb07e3 // 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/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.2.12 // indirect github.com/ugorji/go/codec v1.2.12 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect

5
go.sum
View File

@ -108,6 +108,8 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 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.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= 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 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= 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 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
@ -152,6 +154,7 @@ 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/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-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-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.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 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
@ -163,6 +166,7 @@ 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/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-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-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-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-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-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@ -175,6 +179,7 @@ golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9sn
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 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.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.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.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= 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 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=

144
login.go
View File

@ -7,12 +7,110 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"io" "io"
"io/ioutil"
"net/http" "net/http"
"github.com/edgedb/edgedb-go" "github.com/edgedb/edgedb-go"
"github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2"
) )
type DiscoveryDocument struct {
UserInfoEndpoint string `json:"userinfo_endpoint"`
}
type UserProfile struct {
Email string `json:"email"`
Name string `json:"name"`
}
func getGoogleUserProfile(providerToken string) (string, string) {
// Fetch the discovery document
resp, err := http.Get("https://accounts.google.com/.well-known/openid-configuration")
if err != nil {
panic(fmt.Sprintf("failed to fetch discovery document: %v", err))
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
panic(fmt.Sprintf("failed to fetch discovery document: status code %d", resp.StatusCode))
}
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
panic(fmt.Sprintf("failed to read discovery document response: %v", err))
}
var discoveryDocument DiscoveryDocument
if err := json.Unmarshal(body, &discoveryDocument); err != nil {
panic(fmt.Sprintf("failed to unmarshal discovery document: %v", err))
}
// Fetch the user profile
req, err := http.NewRequest("GET", discoveryDocument.UserInfoEndpoint, nil)
if err != nil {
panic(fmt.Sprintf("failed to create user profile request: %v", err))
}
req.Header.Set("Authorization", "Bearer "+providerToken)
req.Header.Set("Accept", "application/json")
client := &http.Client{}
resp, err = client.Do(req)
if err != nil {
panic(fmt.Sprintf("failed to fetch user profile: %v", err))
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
panic(fmt.Sprintf("failed to fetch user profile: status code %d", resp.StatusCode))
}
body, err = ioutil.ReadAll(resp.Body)
if err != nil {
panic(fmt.Sprintf("failed to read user profile response: %v", err))
}
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
}
func getGitHubUserProfile(providerToken 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 {
panic(fmt.Sprintf("failed to create user profile request: %v", err))
}
req.Header.Set("Authorization", "Bearer "+providerToken)
req.Header.Set("Accept", "application/vnd.github+json")
req.Header.Set("X-GitHub-Api-Version", "2022-11-28")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
panic(fmt.Sprintf("failed to fetch user profile: %v", err))
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
panic(fmt.Sprintf("failed to fetch user profile: status code %d", resp.StatusCode))
}
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
panic(fmt.Sprintf("failed to read user profile response: %v", err))
}
var userProfile GitHubUserProfile
if err := json.Unmarshal(body, &userProfile); err != nil {
panic(fmt.Sprintf("failed to unmarshal user profile: %v", err))
}
return userProfile.Email, userProfile.Name
}
const EDGEDB_AUTH_BASE_URL = "http://127.0.0.1:10700/db/main/ext/auth" const EDGEDB_AUTH_BASE_URL = "http://127.0.0.1:10700/db/main/ext/auth"
func generatePKCE() (string, string) { func generatePKCE() (string, string) {
@ -67,8 +165,9 @@ func handleCallbackSignup(c *fiber.Ctx) error {
} }
var tokenResponse struct { var tokenResponse struct {
AuthToken string `json:"auth_token"` AuthToken string `json:"auth_token"`
IdentityID string `json:"identity_id"` IdentityID string `json:"identity_id"`
ProviderToken string `json:"provider_token"`
} }
err = json.NewDecoder(resp.Body).Decode(&tokenResponse) err = json.NewDecoder(resp.Body).Decode(&tokenResponse)
if err != nil { if err != nil {
@ -84,9 +183,45 @@ func handleCallbackSignup(c *fiber.Ctx) error {
SameSite: "Strict", SameSite: "Strict",
}) })
// Get the issuer of the identity
var identity Identity
identityUUID, err := edgedb.ParseUUID(tokenResponse.IdentityID)
if err != nil {
panic(err)
}
err = edgeClient.QuerySingle(edgeCtx, `
SELECT ext::auth::Identity {
issuer
} FILTER .id = <uuid>$0
`, &identity, identityUUID)
if err != nil {
panic(err)
}
var (
providerEmail string
providerName string
)
// Get the email and name from the provider
if identity.Issuer == "https://accounts.google.com" {
providerEmail, providerName = getGoogleUserProfile(tokenResponse.ProviderToken)
} else if identity.Issuer == "https://github.com" {
providerEmail, providerName = getGithubUserProfile(tokenResponse.ProviderToken)
}
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 // Create a new User and attach the identity
var identityUUID edgedb.UUID
identityUUID, err = edgedb.ParseUUID(tokenResponse.IdentityID)
if err != nil { if err != nil {
return c.Status(fiber.StatusInternalServerError).SendString(fmt.Sprintf("Error in edgedb.ParseUUID: in handleCallbackSignup: %s", err.Error())) return c.Status(fiber.StatusInternalServerError).SendString(fmt.Sprintf("Error in edgedb.ParseUUID: in handleCallbackSignup: %s", err.Error()))
} }
@ -119,7 +254,6 @@ func handleCallback(c *fiber.Ctx) error {
verifier := c.Cookies("jade-edgedb-pkce-verifier", "") verifier := c.Cookies("jade-edgedb-pkce-verifier", "")
if verifier == "" { if verifier == "" {
fmt.Println("Could not find 'verifier' in the cookie store. Is this the same user agent/browser that started the authorization flow?")
return c.Status(fiber.StatusBadRequest).SendString("Could not find 'verifier' in the cookie store. Is this the same user agent/browser that started the authorization flow?") return c.Status(fiber.StatusBadRequest).SendString("Could not find 'verifier' in the cookie store. Is this the same user agent/browser that started the authorization flow?")
} }

12
main.go
View File

@ -96,11 +96,6 @@ func main() {
app.Get("deleteLLM", deleteLLM) app.Get("deleteLLM", deleteLLM)
app.Post("/createLLM", createLLM) app.Post("/createLLM", createLLM)
app.Get("/test", func(c *fiber.Ctx) error {
fmt.Println("Hello from test")
return c.SendString("")
})
app.Get("/empty", func(c *fiber.Ctx) error { app.Get("/empty", func(c *fiber.Ctx) error {
return c.SendString("") return c.SendString("")
}) })
@ -155,7 +150,6 @@ func addKeys(c *fiber.Ctx) error {
// Handle OpenAI key // Handle OpenAI key
if openaiKey != "" { if openaiKey != "" {
if !TestOpenaiKey(openaiKey) { if !TestOpenaiKey(openaiKey) {
fmt.Println("Invalid OpenAI API Key")
return c.SendString("Invalid OpenAI API Key\n") return c.SendString("Invalid OpenAI API Key\n")
} }
@ -171,7 +165,6 @@ func addKeys(c *fiber.Ctx) error {
} }
if Exists { if Exists {
fmt.Println("Company key already exists")
err = edgeClient.Execute(edgeCtx, ` err = edgeClient.Execute(edgeCtx, `
UPDATE Key filter .company.name = <str>$0 AND .key = <str>$1 UPDATE Key filter .company.name = <str>$0 AND .key = <str>$1
SET { SET {
@ -204,7 +197,6 @@ func addKeys(c *fiber.Ctx) error {
// Handle Anthropic key // Handle Anthropic key
if anthropicKey != "" { if anthropicKey != "" {
if !TestAnthropicKey(anthropicKey) { if !TestAnthropicKey(anthropicKey) {
fmt.Println("Invalid Anthropic API Key")
return c.SendString("Invalid Anthropic API Key\n") return c.SendString("Invalid Anthropic API Key\n")
} }
@ -252,7 +244,6 @@ func addKeys(c *fiber.Ctx) error {
// Handle Mistral key // Handle Mistral key
if mistralKey != "" { if mistralKey != "" {
if !TestMistralKey(mistralKey) { if !TestMistralKey(mistralKey) {
fmt.Println("Invalid Mistral API Key")
return c.SendString("Invalid Mistral API Key\n") return c.SendString("Invalid Mistral API Key\n")
} }
@ -300,7 +291,6 @@ func addKeys(c *fiber.Ctx) error {
// Handle Groq key // Handle Groq key
if groqKey != "" { if groqKey != "" {
if !TestGroqKey(groqKey) { if !TestGroqKey(groqKey) {
fmt.Println("Invalid Groq API Key")
return c.SendString("Invalid Groq API Key\n") return c.SendString("Invalid Groq API Key\n")
} }
@ -348,7 +338,6 @@ func addKeys(c *fiber.Ctx) error {
// Handle Gooseai key // Handle Gooseai key
if gooseaiKey != "" { if gooseaiKey != "" {
if !TestGooseaiKey(gooseaiKey) { if !TestGooseaiKey(gooseaiKey) {
fmt.Println("Invalid Gooseai API Key")
return c.SendString("Invalid Gooseai API Key\n") return c.SendString("Invalid Gooseai API Key\n")
} }
@ -396,7 +385,6 @@ func addKeys(c *fiber.Ctx) error {
// Handle Google key // Handle Google key
if googleKey != "" { if googleKey != "" {
if !TestGoogleKey(googleKey) { if !TestGoogleKey(googleKey) {
fmt.Println("Invalid Google API Key")
return c.SendString("Invalid Google API Key\n") return c.SendString("Invalid Google API Key\n")
} }

View File

@ -1,6 +1,7 @@
<div class="chat-container mt-5"> <div class="chat-container mt-5">
<hx hx-get="/loadChat" hx-trigger="load once" hx-swap="outerHTML"></hx> <hx hx-get="/loadChat" hx-trigger="load once" hx-swap="outerHTML"></hx>
{% if IsSubscribed or not IsLimiteReached %}
<div class="chat-input-container mb-5"> <div class="chat-input-container mb-5">
<div class="textarea-wrapper"> <div class="textarea-wrapper">
<textarea {% if not IsLogin or not HaveKey %}disabled{% endif %} class="textarea has-fixed-size" <textarea {% if not IsLogin or not HaveKey %}disabled{% endif %} class="textarea has-fixed-size"
@ -34,6 +35,7 @@
</div> </div>
</div> </div>
</div> </div>
{% endif %}
</div> </div>
<script> <script>

View File

@ -118,14 +118,14 @@
}); });
document.addEventListener('keydown', function (event) { document.addEventListener('keydown', function (event) {
if (event.key === 'Shift' && document.activeElement.id !== 'chat-input-textarea' && document.getElementById('models-creation').classList.contains('is-hidden')) { if (event.key === 'Shift' && document.activeElement.id !== 'chat-input-textarea' && document.getElementById('models-creation').classList.contains('is-hidden') && document.getElementById('models-dropdown').classList.contains('is-active')) {
document.body.classList.add('shift-pressed'); document.body.classList.add('shift-pressed');
} }
}); });
document.addEventListener('keyup', function (event) { document.addEventListener('keyup', function (event) {
// If Shift is press and id="chat-input-textarea" not focused // If Shift is press and id="chat-input-textarea" not focused
if (event.key === 'Shift' && document.activeElement.id !== 'chat-input-textarea' && document.getElementById('models-creation').classList.contains('is-hidden')) { if (event.key === 'Shift' && document.activeElement.id !== 'chat-input-textarea' && document.getElementById('models-creation').classList.contains('is-hidden') && document.getElementById('models-dropdown').classList.contains('is-active')) {
document.body.classList.remove('shift-pressed'); document.body.classList.remove('shift-pressed');
lastSelectedIndex = null; lastSelectedIndex = null;

View File

@ -92,6 +92,14 @@
</form> </form>
<p id="api-keys-status"></p> <p id="api-keys-status"></p>
</div> </div>
{% if IsSub %}
<a class="button is-small mt-1" href="/subscribe">
<span class="icon is-small" style="color: #b00202">
<i class="fa-solid fa-heart"></i>
</span>
<span>Manage subscription</span>
</a>
{% else %}
<a class="button is-small mt-1" hx-get="/pricingTable" hx-target="#chat-container" hx-swap="outerHTML" <a class="button is-small mt-1" hx-get="/pricingTable" hx-target="#chat-container" hx-swap="outerHTML"
hx-trigger="click"> hx-trigger="click">
<span class="icon is-small"> <span class="icon is-small">
@ -99,6 +107,7 @@
</span> </span>
<span>Subscribe to JADE</span> <span>Subscribe to JADE</span>
</a> </a>
{% endif %}
<a class="button is-small mt-1" href="/signout"> <a class="button is-small mt-1" href="/signout">
<span class="icon is-small"> <span class="icon is-small">
<i class="fa-solid fa-right-from-bracket"></i> <i class="fa-solid fa-right-from-bracket"></i>

View File

@ -9,7 +9,7 @@
<div class="dropdown-content"> <div class="dropdown-content">
<div class="dropdown-item"> <div class="dropdown-item">
<!-- Placeholder for additional text --> <!-- Placeholder for additional text -->
<div class="content" style="max-height: 50vh; overflow-y: auto;"> <div class="content" style="max-height: 30vh; overflow-y: auto;">
<table class="table is-narrow is-fullwidth is-striped"> <table class="table is-narrow is-fullwidth is-striped">
<tbody> <tbody>
{% for usage in usages %} {% for usage in usages %}