Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion cmd/status.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ var (
statusCmd = &cobra.Command{
GroupID: groupLocalDev,
Use: "status",
Short: "Show status of local Supabase containers",
Short: "Show status of your Supabase project",
PreRunE: func(cmd *cobra.Command, args []string) error {
es, err := env.EnvironToEnvSet(override)
if err != nil {
Expand All @@ -38,5 +38,9 @@ var (
func init() {
flags := statusCmd.Flags()
flags.StringSliceVar(&override, "override-name", []string{}, "Override specific variable names.")
flags.String("db-url", "", "Shows the database status specified by the connection string (must be percent-encoded).")
flags.Bool("linked", false, "Shows status of the linked project.")
flags.Bool("local", true, "Shows status of the local database.")
statusCmd.MarkFlagsMutuallyExclusive("db-url", "linked", "local")
rootCmd.AddCommand(statusCmd)
}
48 changes: 46 additions & 2 deletions internal/status/status.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,10 @@ import (
"github.com/olekukonko/tablewriter"
"github.com/olekukonko/tablewriter/tw"
"github.com/spf13/afero"
"github.com/supabase/cli/internal/projects/apiKeys"
"github.com/supabase/cli/internal/utils"
"github.com/supabase/cli/internal/utils/flags"
"github.com/supabase/cli/pkg/api"
"github.com/supabase/cli/pkg/fetcher"
)

Expand All @@ -49,7 +51,7 @@ type CustomName struct {

func (c *CustomName) toValues(exclude ...string) map[string]string {
values := map[string]string{
c.DbURL: fmt.Sprintf("postgresql://%s@%s:%d/postgres", url.UserPassword("postgres", utils.Config.Db.Password), utils.Config.Hostname, utils.Config.Db.Port),
c.DbURL: utils.ToPostgresURL(flags.DbConfig),
}

apiEnabled := utils.Config.Api.Enabled && !slices.Contains(exclude, utils.RestId) && !slices.Contains(exclude, utils.ShortContainerImageName(utils.Config.Api.Image))
Expand Down Expand Up @@ -98,7 +100,18 @@ func Run(ctx context.Context, names CustomName, format string, fsys afero.Fs) er
if err := flags.LoadConfig(fsys); err != nil {
return err
}
if err := assertContainerHealthy(ctx, utils.DbId); err != nil {
if !utils.IsLocalDatabase(flags.DbConfig) {
apiUrl := url.URL{Scheme: "https", Host: utils.GetSupabaseHost(flags.ProjectRef)}
utils.Config.Api.ExternalUrl = apiUrl.String()
if err := updateApiKey(ctx); err != nil {
return err
}
if format == utils.OutputPretty {
PrettyPrint(os.Stdout)
return nil
}
return printStatus(names, format, os.Stdout)
} else if err := assertContainerHealthy(ctx, utils.DbId); err != nil {
return err
}
stopped, err := checkServiceHealth(ctx)
Expand All @@ -116,6 +129,37 @@ func Run(ctx context.Context, names CustomName, format string, fsys afero.Fs) er
return printStatus(names, format, os.Stdout, stopped...)
}

func updateApiKey(ctx context.Context) error {
keys, err := apiKeys.RunGetApiKeys(ctx, flags.ProjectRef)
if err != nil {
return err
}
for _, k := range keys {
kt, err := k.Type.Get()
if err != nil {
continue
}
kv, err := k.ApiKey.Get()
if err != nil {
continue
}
switch kt {
case api.ApiKeyResponseTypePublishable:
utils.Config.Auth.PublishableKey.Value = kv
case api.ApiKeyResponseTypeSecret:
utils.Config.Auth.SecretKey.Value = kv
case api.ApiKeyResponseTypeLegacy:
switch k.Name {
case "anon":
utils.Config.Auth.AnonKey.Value = kv
case "service_role":
utils.Config.Auth.ServiceRoleKey.Value = kv
}
}
}
return nil
}

func checkServiceHealth(ctx context.Context) ([]string, error) {
resp, err := utils.Docker.ContainerList(ctx, container.ListOptions{
Filters: utils.CliProjectFilter(utils.Config.ProjectId),
Expand Down
63 changes: 63 additions & 0 deletions internal/status/status_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import (
"github.com/stretchr/testify/require"
"github.com/supabase/cli/internal/testing/apitest"
"github.com/supabase/cli/internal/utils"
"github.com/supabase/cli/internal/utils/flags"
"github.com/supabase/cli/pkg/api"
)

func TestStatusCommand(t *testing.T) {
Expand Down Expand Up @@ -176,3 +178,64 @@ func TestPrintStatus(t *testing.T) {
assert.Equal(t, "DB_URL = \"postgresql://postgres:postgres@127.0.0.1:0/postgres\"\n", stdout.String())
})
}

func TestRemoteStatusCommand(t *testing.T) {
t.Run("shows remote health status", func(t *testing.T) {
// Setup in-memory fs
fsys := afero.NewMemMapFs()
projectRef := apitest.RandomProjectRef()
require.NoError(t, afero.WriteFile(fsys, utils.ProjectRefPath, []byte(projectRef), 0644))
// Setup access token
token := apitest.RandomAccessToken(t)
t.Setenv("SUPABASE_ACCESS_TOKEN", string(token))
// Setup mock API
defer gock.OffAll()
gock.New(utils.DefaultApiHost).
Get("/v1/projects/" + projectRef + "/health").
ParamPresent("services").
Reply(http.StatusOK).
JSON([]api.V1ServiceHealthResponse{
{
Name: api.V1ServiceHealthResponseNameAuth,
Healthy: true,
Status: api.ACTIVEHEALTHY,
},
{
Name: api.V1ServiceHealthResponseNameRealtime,
Healthy: true,
Status: api.ACTIVEHEALTHY,
},
{
Name: api.V1ServiceHealthResponseNameRest,
Healthy: true,
Status: api.ACTIVEHEALTHY,
},
{
Name: api.V1ServiceHealthResponseNameStorage,
Healthy: true,
Status: api.ACTIVEHEALTHY,
},
{
Name: api.V1ServiceHealthResponseNameDb,
Healthy: true,
Status: api.ACTIVEHEALTHY,
},
})
// Run test
assert.NoError(t, Run(context.Background(), CustomName{}, utils.OutputPretty, fsys))
// Check error
assert.Empty(t, apitest.ListUnmatchedRequests())
})

t.Run("throws error on missing project ref", func(t *testing.T) {
// Setup in-memory fs
fsys := afero.NewMemMapFs()
defer gock.OffAll()
// Reset global state
flags.ProjectRef = ""
// Run test
err := Run(context.Background(), CustomName{}, utils.OutputPretty, fsys)
// Check error
assert.ErrorContains(t, err, "project ref")
})
}
Loading