Skip to content

Commit 7f472f4

Browse files
committed
chore: follow documentation for defining config
1 parent 94abc3e commit 7f472f4

File tree

2 files changed

+103
-119
lines changed

2 files changed

+103
-119
lines changed

internal/utils/config.go

Lines changed: 101 additions & 116 deletions
Original file line numberDiff line numberDiff line change
@@ -44,26 +44,6 @@ var (
4444
//go:embed templates/initial_schemas/15.sql
4545
InitialSchemaPg15Sql string
4646

47-
authExternalProviders = []string{
48-
"apple",
49-
"azure",
50-
"bitbucket",
51-
"discord",
52-
"facebook",
53-
"github",
54-
"gitlab",
55-
"google",
56-
"keycloak",
57-
"linkedin",
58-
"notion",
59-
"twitch",
60-
"twitter",
61-
"slack",
62-
"spotify",
63-
"workos",
64-
"zoom",
65-
}
66-
6747
//go:embed templates/init_config.toml
6848
initConfigEmbed string
6949
initConfigTemplate = template.Must(template.New("initConfig").Parse(initConfigEmbed))
@@ -82,17 +62,60 @@ func (s *sizeInBytes) UnmarshalText(text []byte) error {
8262
return err
8363
}
8464

85-
var Config config
65+
var Config = config{
66+
Auth: auth{
67+
External: map[string]provider{
68+
"apple": {},
69+
"azure": {},
70+
"bitbucket": {},
71+
"discord": {},
72+
"facebook": {},
73+
"github": {},
74+
"gitlab": {},
75+
"google": {},
76+
"keycloak": {},
77+
"linkedin": {},
78+
"notion": {},
79+
"twitch": {},
80+
"twitter": {},
81+
"slack": {},
82+
"spotify": {},
83+
"workos": {},
84+
"zoom": {},
85+
},
86+
JwtSecret: "super-secret-jwt-token-with-at-least-32-characters-long",
87+
AnonKey: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24iLCJleHAiOjE5ODM4MTI5OTZ9.CRXP1A7WOeoJeXxjNni43kdQwgnWNReilDMblYTn_I0",
88+
ServiceRoleKey: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6InNlcnZpY2Vfcm9sZSIsImV4cCI6MTk4MzgxMjk5Nn0.EGIM96RAZx35lJzdJsyH-qQwv8Hdp7fsn3W0YpN81IU",
89+
},
90+
Db: db{
91+
Password: "postgres",
92+
},
93+
Analytics: analytics{
94+
ApiKey: "api-key",
95+
},
96+
}
8697

8798
// We follow these rules when adding new config:
8899
// 1. Update init_config.toml with the new key, default value, and comments to explain usage.
89-
// 2. Update config struct with new field and toml tag, written out in snake_case.
90-
// 3. Add custom validations to LoadConfigFS function for the new field, such as range checks.
100+
// 2. Update config struct with new field and toml tag (spelled in snake_case).
101+
// 3. Add custom field validations to LoadConfigFS function for eg. integer range checks.
102+
//
103+
// If you are adding new user defined secrets, such as OAuth provider secret, the default value in
104+
// init_config.toml should be an env var substitution. For example,
105+
//
106+
// > secret = "env(SUPABASE_AUTH_EXTERNAL_APPLE_SECRET)"
91107
//
92-
// If you are adding new secrets, such as API keys, use env var instead of toml. For example,
93-
// 1. Config.Auth.AnonKey is tagged with `toml:"-" mapstructure:"anon_key"`. This tag prevents
94-
// externalising to toml but allows reading from an env var named SUPABASE_AUTH_ANON_KEY.
95-
// 2. Default values should be added to LoadConfigFS function, after checking for empty value.
108+
// If you are adding an internal config or secret that doesn't need to be overridden by the user,
109+
// exclude the field from toml serialization. For example,
110+
//
111+
// type auth struct {
112+
// AnonKey string `toml:"-" mapstructure:"anon_key"`
113+
// }
114+
//
115+
// Use `mapstructure:"anon_key"` tag only if you want inject values from a predictable environment
116+
// variable, such as SUPABASE_AUTH_ANON_KEY.
117+
//
118+
// Default values for internal configs should be added to `var Config` initialiser.
96119
type (
97120
config struct {
98121
ProjectId string `toml:"project_id"`
@@ -273,12 +296,14 @@ func LoadConfigFS(fsys afero.Fs) error {
273296
LogflareId = "supabase_analytics_" + Config.ProjectId
274297
VectorId = "supabase_vector_" + Config.ProjectId
275298
}
299+
// Validate api config
276300
if Config.Api.Port == 0 {
277301
return errors.New("Missing required field in config: api.port")
278302
}
279303
// Append required schemas if they are missing
280304
Config.Api.Schemas = removeDuplicates(append([]string{"public", "storage"}, Config.Api.Schemas...))
281305
Config.Api.ExtraSearchPath = removeDuplicates(append([]string{"public"}, Config.Api.ExtraSearchPath...))
306+
// Validate db config
282307
if Config.Db.Port == 0 {
283308
return errors.New("Missing required field in config: db.port")
284309
}
@@ -299,28 +324,20 @@ func LoadConfigFS(fsys afero.Fs) error {
299324
default:
300325
return fmt.Errorf("Failed reading config: Invalid %s: %v.", Aqua("db.major_version"), Config.Db.MajorVersion)
301326
}
302-
if Config.Db.Password == "" {
303-
Config.Db.Password = "postgres"
304-
}
327+
// Validate studio config
305328
if Config.Studio.Port == 0 {
306329
return errors.New("Missing required field in config: studio.port")
307330
}
331+
// Validate email config
308332
if Config.Inbucket.Port == 0 {
309333
return errors.New("Missing required field in config: inbucket.port")
310334
}
335+
// Validate auth config
311336
if Config.Auth.SiteUrl == "" {
312337
return errors.New("Missing required field in config: auth.site_url")
313338
}
314-
if Config.Auth.JwtSecret == "" {
315-
Config.Auth.JwtSecret = "super-secret-jwt-token-with-at-least-32-characters-long"
316-
}
317-
if Config.Auth.AnonKey == "" {
318-
Config.Auth.AnonKey = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24iLCJleHAiOjE5ODM4MTI5OTZ9.CRXP1A7WOeoJeXxjNni43kdQwgnWNReilDMblYTn_I0"
319-
}
320-
if Config.Auth.ServiceRoleKey == "" {
321-
Config.Auth.ServiceRoleKey = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6InNlcnZpY2Vfcm9sZSIsImV4cCI6MTk4MzgxMjk5Nn0.EGIM96RAZx35lJzdJsyH-qQwv8Hdp7fsn3W0YpN81IU"
322-
}
323-
339+
// Validate sms config
340+
var err error
324341
if Config.Auth.Sms.Twilio.Enabled {
325342
if len(Config.Auth.Sms.Twilio.AccountSid) == 0 {
326343
return errors.New("Missing required field in config: auth.sms.twilio.account_sid")
@@ -331,6 +348,9 @@ func LoadConfigFS(fsys afero.Fs) error {
331348
if len(Config.Auth.Sms.Twilio.AuthToken) == 0 {
332349
return errors.New("Missing required field in config: auth.sms.twilio.auth_token")
333350
}
351+
if Config.Auth.Sms.Twilio.AuthToken, err = maybeLoadEnv(Config.Auth.Sms.Twilio.AuthToken); err != nil {
352+
return err
353+
}
334354
}
335355
if Config.Auth.Sms.Messagebird.Enabled {
336356
if len(Config.Auth.Sms.Messagebird.Originator) == 0 {
@@ -339,6 +359,9 @@ func LoadConfigFS(fsys afero.Fs) error {
339359
if len(Config.Auth.Sms.Messagebird.AccessKey) == 0 {
340360
return errors.New("Missing required field in config: auth.sms.messagebird.access_key")
341361
}
362+
if Config.Auth.Sms.Messagebird.AccessKey, err = maybeLoadEnv(Config.Auth.Sms.Messagebird.AccessKey); err != nil {
363+
return err
364+
}
342365
}
343366
if Config.Auth.Sms.Textlocal.Enabled {
344367
if len(Config.Auth.Sms.Textlocal.Sender) == 0 {
@@ -347,6 +370,9 @@ func LoadConfigFS(fsys afero.Fs) error {
347370
if len(Config.Auth.Sms.Textlocal.ApiKey) == 0 {
348371
return errors.New("Missing required field in config: auth.sms.textlocal.api_key")
349372
}
373+
if Config.Auth.Sms.Textlocal.ApiKey, err = maybeLoadEnv(Config.Auth.Sms.Textlocal.ApiKey); err != nil {
374+
return err
375+
}
350376
}
351377
if Config.Auth.Sms.Vonage.Enabled {
352378
if len(Config.Auth.Sms.Vonage.From) == 0 {
@@ -358,97 +384,56 @@ func LoadConfigFS(fsys afero.Fs) error {
358384
if len(Config.Auth.Sms.Vonage.ApiSecret) == 0 {
359385
return errors.New("Missing required field in config: auth.sms.vonage.api_secret")
360386
}
387+
if Config.Auth.Sms.Vonage.ApiKey, err = maybeLoadEnv(Config.Auth.Sms.Vonage.ApiKey); err != nil {
388+
return err
389+
}
390+
if Config.Auth.Sms.Vonage.ApiSecret, err = maybeLoadEnv(Config.Auth.Sms.Vonage.ApiSecret); err != nil {
391+
return err
392+
}
361393
}
362-
363-
if Config.Auth.External == nil {
364-
Config.Auth.External = map[string]provider{}
365-
}
366-
for _, ext := range authExternalProviders {
367-
if _, ok := Config.Auth.External[ext]; !ok {
368-
Config.Auth.External[ext] = provider{
369-
Enabled: false,
370-
ClientId: "",
371-
Secret: "",
372-
}
373-
} else if Config.Auth.External[ext].Enabled {
374-
var clientId, secret, redirectUri, url string
375-
376-
if Config.Auth.External[ext].ClientId == "" {
377-
return fmt.Errorf("Missing required field in config: auth.external.%s.client_id", ext)
378-
} else {
379-
v, err := maybeLoadEnv(Config.Auth.External[ext].ClientId)
380-
if err != nil {
381-
return err
382-
}
383-
clientId = v
384-
}
385-
if Config.Auth.External[ext].Secret == "" {
386-
return fmt.Errorf("Missing required field in config: auth.external.%s.secret", ext)
387-
} else {
388-
v, err := maybeLoadEnv(Config.Auth.External[ext].Secret)
389-
if err != nil {
390-
return err
391-
}
392-
secret = v
393-
}
394-
if Config.Auth.External[ext].RedirectUri != "" {
395-
v, err := maybeLoadEnv(Config.Auth.External[ext].RedirectUri)
396-
if err != nil {
397-
return err
398-
}
399-
redirectUri = v
400-
}
401-
if Config.Auth.External[ext].Url != "" {
402-
v, err := maybeLoadEnv(Config.Auth.External[ext].Url)
403-
if err != nil {
404-
return err
405-
}
406-
url = v
407-
}
408-
409-
Config.Auth.External[ext] = provider{
410-
Enabled: true,
411-
ClientId: clientId,
412-
Secret: secret,
413-
RedirectUri: redirectUri,
414-
Url: url,
415-
}
394+
// Validate oauth config
395+
for ext, provider := range Config.Auth.External {
396+
if !provider.Enabled {
397+
continue
398+
}
399+
if provider.ClientId == "" {
400+
return fmt.Errorf("Missing required field in config: auth.external.%s.client_id", ext)
401+
}
402+
if provider.Secret == "" {
403+
return fmt.Errorf("Missing required field in config: auth.external.%s.secret", ext)
404+
}
405+
if provider.ClientId, err = maybeLoadEnv(provider.ClientId); err != nil {
406+
return err
416407
}
408+
if provider.Secret, err = maybeLoadEnv(provider.Secret); err != nil {
409+
return err
410+
}
411+
if provider.RedirectUri, err = maybeLoadEnv(provider.RedirectUri); err != nil {
412+
return err
413+
}
414+
if provider.Url, err = maybeLoadEnv(provider.Url); err != nil {
415+
return err
416+
}
417+
Config.Auth.External[ext] = provider
417418
}
418419
}
419-
420-
if Config.Functions == nil {
421-
Config.Functions = map[string]function{}
422-
}
420+
// Validate functions config
423421
for name, functionConfig := range Config.Functions {
424-
verifyJWT := functionConfig.VerifyJWT
425-
426-
if verifyJWT == nil {
427-
x := true
428-
verifyJWT = &x
429-
}
430-
431-
Config.Functions[name] = function{
432-
VerifyJWT: verifyJWT,
433-
ImportMap: functionConfig.ImportMap,
422+
if functionConfig.VerifyJWT == nil {
423+
verifyJWT := true
424+
functionConfig.VerifyJWT = &verifyJWT
425+
Config.Functions[name] = functionConfig
434426
}
435427
}
436-
428+
// Validate logflare config
437429
if Config.Analytics.Enabled {
438430
if len(Config.Analytics.GcpProjectId) == 0 {
439431
return errors.New("Missing required field in config: analytics.gcp_project_id")
440432
}
441433
if len(Config.Analytics.GcpProjectNumber) == 0 {
442434
return errors.New("Missing required field in config: analytics.gcp_project_number")
443435
}
444-
if len(Config.Analytics.GcpJwtPath) == 0 {
445-
Config.Analytics.GcpJwtPath = "supabase/gcloud.json"
446-
}
447-
if len(Config.Analytics.ApiKey) == 0 {
448-
Config.Analytics.ApiKey = "api-key"
449-
}
450436
}
451-
452437
return nil
453438
}
454439

internal/utils/templates/init_config.toml

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -83,9 +83,8 @@ enable_confirmations = false
8383
enabled = false
8484
account_sid = ""
8585
message_service_sid = ""
86-
# DO NOT commit your Twilio auth token to git. You can leave this field blank and define the
87-
# following environment variable instead: SUPABASE_AUTH_SMS_TWILIO_AUTH_TOKEN
88-
auth_token = ""
86+
# DO NOT commit your Twilio auth token to git. Use environment variable substitution instead:
87+
auth_token = "env(SUPABASE_AUTH_SMS_TWILIO_AUTH_TOKEN)"
8988

9089
# Use an external OAuth provider. The full list of providers are: `apple`, `azure`, `bitbucket`,
9190
# `discord`, `facebook`, `github`, `gitlab`, `google`, `keycloak`, `linkedin`, `notion`, `twitch`,

0 commit comments

Comments
 (0)