-
Notifications
You must be signed in to change notification settings - Fork 21
/
Copy pathclient.go
166 lines (140 loc) · 4.44 KB
/
client.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
package supabase
import (
"errors"
"log"
"time"
"github.com/supabase-community/functions-go"
"github.com/supabase-community/gotrue-go"
"github.com/supabase-community/gotrue-go/types"
postgrest "github.com/supabase-community/postgrest-go"
storage_go "github.com/supabase-community/storage-go"
)
const (
REST_URL = "/rest/v1"
STORAGE_URL = "/storage/v1"
AUTH_URL = "/auth/v1"
FUNCTIONS_URL = "/functions/v1"
)
type Client struct {
// Why is this a private field??
rest *postgrest.Client
Storage *storage_go.Client
// Auth is an interface. We don't need a pointer to an interface.
Auth gotrue.Client
Functions *functions.Client
options clientOptions
}
type clientOptions struct {
url string
headers map[string]string
}
type ClientOptions struct {
Headers map[string]string
Schema string
}
// NewClient creates a new Supabase client.
// url is the Supabase URL.
// key is the Supabase API key.
// options is the Supabase client options.
func NewClient(url, key string, options *ClientOptions) (*Client, error) {
if url == "" || key == "" {
return nil, errors.New("url and key are required")
}
headers := map[string]string{
"Authorization": "Bearer " + key,
"apikey": key,
}
if options != nil && options.Headers != nil {
for k, v := range options.Headers {
headers[k] = v
}
}
client := &Client{}
client.options.url = url
// map is pass by reference, so this gets updated by rest of function
client.options.headers = headers
var schema string
if options != nil && options.Schema != "" {
schema = options.Schema
} else {
schema = "public"
}
client.rest = postgrest.NewClient(url+REST_URL, schema, headers)
client.Storage = storage_go.NewClient(url+STORAGE_URL, key, headers)
// ugly to make auth client use custom URL
tmp := gotrue.New(url, key)
client.Auth = tmp.WithCustomGoTrueURL(url + AUTH_URL)
client.Functions = functions.NewClient(url+FUNCTIONS_URL, key, headers)
return client, nil
}
// Wrap postgrest From method
// From returns a QueryBuilder for the specified table.
func (c *Client) From(table string) *postgrest.QueryBuilder {
return c.rest.From(table)
}
// Wrap postgrest Rpc method
// Rpc returns a string for the specified function.
func (c *Client) Rpc(name, count string, rpcBody interface{}) string {
return c.rest.Rpc(name, count, rpcBody)
}
func (c *Client) SignInWithEmailPassword(email, password string) (types.Session, error) {
token, err := c.Auth.SignInWithEmailPassword(email, password)
if err != nil {
return types.Session{}, err
}
c.UpdateAuthSession(token.Session)
return token.Session, err
}
func (c *Client) SignInWithPhonePassword(phone, password string) (types.Session, error) {
token, err := c.Auth.SignInWithPhonePassword(phone, password)
if err != nil {
return types.Session{}, err
}
c.UpdateAuthSession(token.Session)
return token.Session, err
}
func (c *Client) EnableTokenAutoRefresh(session types.Session) {
go func() {
attempt := 0
expiresAt := time.Now().Add(time.Duration(session.ExpiresIn) * time.Second)
for {
sleepDuration := (time.Until(expiresAt) / 4) * 3
if sleepDuration > 0 {
time.Sleep(sleepDuration)
}
// Refresh the token
newSession, err := c.RefreshToken(session.RefreshToken)
if err != nil {
attempt++
if attempt <= 3 {
log.Printf("Error refreshing token, retrying with exponential backoff: %v", err)
time.Sleep(time.Duration(1<<attempt) * time.Second)
} else {
log.Printf("Error refreshing token, retrying every 30 seconds: %v", err)
time.Sleep(30 * time.Second)
}
continue
}
// Update the session, reset the attempt counter, and update the expiresAt time
c.UpdateAuthSession(newSession)
session = newSession
attempt = 0
expiresAt = time.Now().Add(time.Duration(session.ExpiresIn) * time.Second)
}
}()
}
func (c *Client) RefreshToken(refreshToken string) (types.Session, error) {
token, err := c.Auth.RefreshToken(refreshToken)
if err != nil {
return types.Session{}, err
}
c.UpdateAuthSession(token.Session)
return token.Session, err
}
func (c *Client) UpdateAuthSession(session types.Session) {
c.Auth = c.Auth.WithToken(session.AccessToken)
c.rest.SetAuthToken(session.AccessToken)
c.options.headers["Authorization"] = "Bearer " + session.AccessToken
c.Storage = storage_go.NewClient(c.options.url+STORAGE_URL, session.AccessToken, c.options.headers)
c.Functions = functions.NewClient(c.options.url+FUNCTIONS_URL, session.AccessToken, c.options.headers)
}