Skip to content

Commit

Permalink
feat: add interactive mode for creating project
Browse files Browse the repository at this point in the history
  • Loading branch information
sweatybridge committed Aug 12, 2022
1 parent 68155b4 commit 5b86847
Show file tree
Hide file tree
Showing 9 changed files with 369 additions and 181 deletions.
142 changes: 105 additions & 37 deletions cmd/projects.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
package cmd

import (
"fmt"
"os"
"os/signal"
"strings"

"github.com/spf13/afero"
"github.com/spf13/cobra"
"github.com/supabase/cli/internal/projects/create"
"github.com/supabase/cli/internal/projects/list"
"github.com/supabase/cli/internal/utils"
"github.com/supabase/cli/pkg/api"
)

var (
Expand All @@ -13,36 +20,37 @@ var (
Short: "Manage Supabase projects",
}

interactive bool
orgId string
dbPassword string
region utils.EnumFlag
plan utils.EnumFlag

projectsCreateCmd = &cobra.Command{
Use: "create",
Use: "create <project name>",
Short: "Create a new project",
Args: cobra.ExactArgs(1),
Example: `supabase projects create my-project --org-id cool-green-pqdr0qc --db-password ******** --region us-east-1`,
PreRun: func(cmd *cobra.Command, args []string) {
if !interactive {
cobra.CheckErr(cmd.MarkFlagRequired("org-id"))
cobra.CheckErr(cmd.MarkFlagRequired("db-password"))
cobra.CheckErr(cmd.MarkFlagRequired("region"))
}
},
RunE: func(cmd *cobra.Command, args []string) error {
name := args[0]
orgId, err := cmd.Flags().GetString("org-id")
if err != nil {
return err
}
dbPassword, err := cmd.Flags().GetString("db-password")
if err != nil {
return err
if interactive {
fmt.Fprintln(os.Stderr, printKeyValue("Creating project", name))
cobra.CheckErr(PromptCreateFlags(cmd))
}
region, err := cmd.Flags().GetString("region")
if err != nil {
return err
}
plan, err := cmd.Flags().GetString("plan")
if err != nil {
return err
}

return create.Run(create.RequestParam{
Name: name,
OrgId: orgId,
DbPass: dbPassword,
Region: region,
Plan: plan,
ctx, _ := signal.NotifyContext(cmd.Context(), os.Interrupt)
return create.Run(ctx, api.CreateProjectBody{
Name: name,
OrganizationId: orgId,
DbPass: dbPassword,
Region: api.CreateProjectBodyRegion(region.Value),
Plan: api.CreateProjectBodyPlan(plan.Value),
}, afero.NewOsFs())
},
}
Expand All @@ -51,27 +59,87 @@ var (
Use: "list",
Short: "List all projects",
RunE: func(cmd *cobra.Command, args []string) error {
return list.Run(afero.NewOsFs())
ctx, _ := signal.NotifyContext(cmd.Context(), os.Interrupt)
return list.Run(ctx, afero.NewOsFs())
},
}
)

func init() {
// TODO: Make these optional once we implement prompting missing flags.
projectsCreateCmd.Flags().String("org-id", "", "Organization ID to create the project in.")
if err := projectsCreateCmd.MarkFlagRequired("org-id"); err != nil {
panic(err)
}
projectsCreateCmd.Flags().String("db-password", "", "Database password of the project.")
if err := projectsCreateCmd.MarkFlagRequired("db-password"); err != nil {
panic(err)
// Setup enum flags
region.Allowed = make([]string, len(utils.RegionMap))
for k := range utils.RegionMap {
region.Allowed = append(region.Allowed, k)
}
projectsCreateCmd.Flags().String("region", "", "Select a region close to you for the best performance.")
if err := projectsCreateCmd.MarkFlagRequired("region"); err != nil {
panic(err)
}
projectsCreateCmd.Flags().String("plan", "free", `Select a plan that suits your needs. Can be "free" or "pro".`)
plan.Allowed = []string{string(api.Free), string(api.Pro)}
plan.Value = string(api.Free)
// Add flags to cobra command
createFlags := projectsCreateCmd.Flags()
createFlags.BoolVarP(&interactive, "interactive", "i", false, "Enables interactive mode.")
createFlags.StringVar(&orgId, "org-id", "", "Organization ID to create the project in.")
createFlags.StringVar(&dbPassword, "db-password", "", "Database password of the project.")
createFlags.Var(&region, "region", "Select a region close to you for the best performance.")
createFlags.Var(&plan, "plan", "Select a plan that suits your needs.")
// Add commands to root
projectsCmd.AddCommand(projectsCreateCmd)
projectsCmd.AddCommand(projectsListCmd)
rootCmd.AddCommand(projectsCmd)
}

func PromptCreateFlags(cmd *cobra.Command) error {
ctx := cmd.Context()
if !cmd.Flags().Changed("org-id") {
title := "Which organisation do you want to create the project for?"
resp, err := utils.GetSupabase().GetOrganizationsWithResponse(ctx)
if err != nil {
return err
}
items := make([]utils.PromptItem, len(*resp.JSON200))
for i, org := range *resp.JSON200 {
items[i] = utils.PromptItem{Summary: org.Name, Details: org.Id}
}
choice, err := utils.PromptChoice(ctx, title, items)
if err != nil {
return err
}
orgId = choice.Details
}
fmt.Fprintln(os.Stderr, printKeyValue("Selected org-id", orgId))
if !cmd.Flags().Changed("region") {
title := "Which region do you want to host the project in?"
items := make([]utils.PromptItem, len(utils.RegionMap))
i := 0
for k, v := range utils.RegionMap {
items[i] = utils.PromptItem{Summary: k, Details: v}
i++
}
choice, err := utils.PromptChoice(ctx, title, items)
if err != nil {
return err
}
region.Value = choice.Summary
}
fmt.Fprintln(os.Stderr, printKeyValue("Selected region", region.Value))
if !cmd.Flags().Changed("plan") {
title := "Do you want a free or pro plan?"
choice, err := utils.PromptChoice(ctx, title, []utils.PromptItem{
{Summary: string(api.Free)},
{Summary: string(api.Pro)},
})
if err != nil {
return err
}
plan.Value = choice.Summary
}
fmt.Fprintln(os.Stderr, printKeyValue("Selected plan", plan.Value))
if dbPassword == "" {
dbPassword = PromptPassword(os.Stdin)
}
return nil
}

func printKeyValue(key, value string) string {
indent := 20 - len(key)
spaces := strings.Repeat(" ", indent)
return key + ":" + spaces + value
}
9 changes: 6 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ go 1.18

require (
github.com/BurntSushi/toml v1.0.0
github.com/charmbracelet/bubbles v0.10.3
github.com/charmbracelet/bubbletea v0.20.0
github.com/charmbracelet/bubbles v0.13.0
github.com/charmbracelet/bubbletea v0.21.0
github.com/charmbracelet/glamour v0.5.0
github.com/charmbracelet/lipgloss v0.5.0
github.com/deepmap/oapi-codegen v1.11.0
Expand All @@ -22,6 +22,7 @@ require (
)

require (
github.com/atotto/clipboard v0.1.4 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/getkin/kin-openapi v0.94.0 // indirect
github.com/ghodss/yaml v1.0.0 // indirect
Expand All @@ -43,8 +44,10 @@ require (
github.com/mattn/go-colorable v0.1.12 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/muesli/cancelreader v0.2.0 // indirect
github.com/pelletier/go-toml/v2 v2.0.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/sahilm/fuzzy v0.1.0 // indirect
github.com/ugorji/go/codec v1.2.7 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasttemplate v1.2.1 // indirect
Expand All @@ -59,7 +62,7 @@ require (
github.com/Microsoft/go-winio v0.5.2 // indirect
github.com/alecthomas/chroma v0.10.0 // indirect
github.com/aymerick/douceur v0.2.0 // indirect
github.com/charmbracelet/harmonica v0.1.0 // indirect
github.com/charmbracelet/harmonica v0.2.0 // indirect
github.com/containerd/console v1.0.3 // indirect
github.com/containerd/containerd v1.6.2 // indirect
github.com/dlclark/regexp2 v1.4.0 // indirect
Expand Down
23 changes: 13 additions & 10 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -48,21 +48,20 @@ github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v
github.com/alecthomas/chroma v0.10.0 h1:7XDcGkCQopCNKjZHfYrNLraA+M7e0fMiJ/Mfikbfjek=
github.com/alecthomas/chroma v0.10.0/go.mod h1:jtJATyUxlIORhUOFNA9NZDWGAQ8wpxQQqNSB4rjA/1s=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4=
github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/charmbracelet/bubbles v0.10.3 h1:fKarbRaObLn/DCsZO4Y3vKCwRUzynQD9L+gGev1E/ho=
github.com/charmbracelet/bubbles v0.10.3/go.mod h1:jOA+DUF1rjZm7gZHcNyIVW+YrBPALKfpGVdJu8UiJsA=
github.com/charmbracelet/bubbletea v0.19.3/go.mod h1:VuXF2pToRxDUHcBUcPmCRUHRvFATM4Ckb/ql1rBl3KA=
github.com/charmbracelet/bubbletea v0.20.0 h1:/b8LEPgCbNr7WWZ2LuE/BV1/r4t5PyYJtDb+J3vpwxc=
github.com/charmbracelet/bubbletea v0.20.0/go.mod h1:zpkze1Rioo4rJELjRyGlm9T2YNou1Fm4LIJQSa5QMEM=
github.com/charmbracelet/bubbles v0.13.0 h1:zP/ROH3wJEBqZWKIsD50ZKKlx3ydLInq3LdD/Nrlb8w=
github.com/charmbracelet/bubbles v0.13.0/go.mod h1:bbeTiXwPww4M031aGi8UK2HT9RDWoiNibae+1yCMtcc=
github.com/charmbracelet/bubbletea v0.21.0 h1:f3y+kanzgev5PA916qxmDybSHU3N804uOnKnhRPXTcI=
github.com/charmbracelet/bubbletea v0.21.0/go.mod h1:GgmJMec61d08zXsOhqRC/AiOx4K4pmz+VIcRIm1FKr4=
github.com/charmbracelet/glamour v0.5.0 h1:wu15ykPdB7X6chxugG/NNfDUbyyrCLV9XBalj5wdu3g=
github.com/charmbracelet/glamour v0.5.0/go.mod h1:9ZRtG19AUIzcTm7FGLGbq3D5WKQ5UyZBbQsMQN0XIqc=
github.com/charmbracelet/harmonica v0.1.0 h1:lFKeSd6OAckQ/CEzPVd2mqj+YMEubQ/3FM2IYY3xNm0=
github.com/charmbracelet/harmonica v0.1.0/go.mod h1:KSri/1RMQOZLbw7AHqgcBycp8pgJnQMYYT8QZRqZ1Ao=
github.com/charmbracelet/lipgloss v0.4.0/go.mod h1:vmdkHvce7UzX6xkyf4cca8WlwdQ5RQr8fzta+xl7BOM=
github.com/charmbracelet/harmonica v0.2.0 h1:8NxJWRWg/bzKqqEaaeFNipOu77YR5t8aSwG4pgaUBiQ=
github.com/charmbracelet/harmonica v0.2.0/go.mod h1:KSri/1RMQOZLbw7AHqgcBycp8pgJnQMYYT8QZRqZ1Ao=
github.com/charmbracelet/lipgloss v0.5.0 h1:lulQHuVeodSgDez+3rGiuxlPVXSnhth442DATR2/8t8=
github.com/charmbracelet/lipgloss v0.5.0/go.mod h1:EZLha/HbzEt7cYqdFPovlqy5FZPj0xFhg5SaqxScmgs=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
Expand All @@ -78,7 +77,6 @@ github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWH
github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I=
github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
github.com/containerd/console v1.0.2/go.mod h1:ytZPjGgY2oeTkAONYafi2kSj0aYggsf8acV1PGKCbzQ=
github.com/containerd/console v1.0.3 h1:lIr7SlA5PxZyMV30bDW0MGbiOPXwc63yRuCP0ARubLw=
github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U=
github.com/containerd/containerd v1.6.2 h1:pcaPUGbYW8kBw6OgIZwIVIeEhdWVrBzsoCfVJ5BjrLU=
Expand Down Expand Up @@ -308,6 +306,7 @@ github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/labstack/echo/v4 v4.7.2 h1:Kv2/p8OaQ+M6Ex4eGimg9b9e6icoxA42JSlOR3msKtI=
github.com/labstack/echo/v4 v4.7.2/go.mod h1:xkCDAdFCIf8jsFQ5NnbK7oqaF/yU1A1X20Ltm0OvSks=
Expand Down Expand Up @@ -376,6 +375,8 @@ github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7P
github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b/go.mod h1:fQuZ0gauxyBcmsdE3ZT4NasjaRdxmbCS0jRHsrWu3Ho=
github.com/muesli/ansi v0.0.0-20211031195517-c9f0611b6c70 h1:kMlmsLSbjkikxQJ1IPwaM+7LJ9ltFu/fi8CRzvSnQmA=
github.com/muesli/ansi v0.0.0-20211031195517-c9f0611b6c70/go.mod h1:fQuZ0gauxyBcmsdE3ZT4NasjaRdxmbCS0jRHsrWu3Ho=
github.com/muesli/cancelreader v0.2.0 h1:SOpr+CfyVNce341kKqvbhhzQhBPyJRXQaCtn03Pae1Q=
github.com/muesli/cancelreader v0.2.0/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo=
github.com/muesli/reflow v0.2.1-0.20210115123740-9e1d0d53df68/go.mod h1:Xk+z4oIWdQqJzsxyjgl3P22oYZnHdZ8FFTHAQQt5BMQ=
github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s=
github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8=
Expand Down Expand Up @@ -416,6 +417,7 @@ github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU=
github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sahilm/fuzzy v0.1.0 h1:FzWGaw2Opqyu+794ZQ9SYifWv2EIXpwP4q8dY1kDAwI=
github.com/sahilm/fuzzy v0.1.0/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y=
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
Expand Down Expand Up @@ -657,11 +659,12 @@ golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220204135822-1c1b9b1eba6a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220513210249-45d2b4557a2a h1:N2T1jUrTQE9Re6TFF5PhvEHXHCguynGhKjWVsIUt5cY=
golang.org/x/sys v0.0.0-20220513210249-45d2b4557a2a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210422114643-f5beecf764ed/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
Expand Down
72 changes: 16 additions & 56 deletions internal/projects/create/create.go
Original file line number Diff line number Diff line change
@@ -1,79 +1,39 @@
package create

import (
"bytes"
"encoding/json"
"context"
"errors"
"fmt"
"io"
"net/http"

"github.com/spf13/afero"
"github.com/supabase/cli/internal/projects/list"
"github.com/supabase/cli/internal/utils"
"github.com/supabase/cli/pkg/api"
)

type RequestParam struct {
OrgId string `json:"organization_id"`
Name string `json:"name"`
DbPass string `json:"db_pass"`
Region string `json:"region"`
Plan string `json:"plan"`
}

func Run(params RequestParam, fsys afero.Fs) error {
accessToken, err := utils.LoadAccessTokenFS(fsys)
if err != nil {
return err
}

func Run(ctx context.Context, params api.CreateProjectBody, fsys afero.Fs) error {
// TODO: Prompt missing args.
{
}

// POST request, check errors
var project list.Project
{
jsonBytes, err := json.Marshal(params)
if err != nil {
return err
}

req, err := http.NewRequest("POST", utils.GetSupabaseAPIHost()+"/v1/projects", bytes.NewReader(jsonBytes))
if err != nil {
return err
}
req.Header.Add("Authorization", "Bearer "+string(accessToken))
req.Header.Add("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()

// TODO: remove the StatusOK case after 2022-08-20
if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusCreated {
body, err := io.ReadAll(resp.Body)
if err != nil {
return fmt.Errorf("Unexpected error creating project: %w", err)
}

return errors.New("Unexpected error creating project: " + string(body))
}
resp, err := utils.GetSupabase().CreateProjectWithResponse(ctx, params)
if err != nil {
return err
}

body, err := io.ReadAll(resp.Body)
if err != nil {
return err
}
if err := json.Unmarshal(body, &project); err != nil {
return fmt.Errorf("Failed to create a new project: %w", err)
}
// TODO: remove the StatusOK case after 2022-08-20
// if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusCreated {
if resp.JSON201 == nil {
return errors.New("Unexpected error creating project: " + string(resp.Body))
}

// TODO: Poll until PostgREST is reachable.
{
}

fmt.Printf("Created a new project %s at %s\n", utils.Aqua(project.Name), utils.Aqua(utils.GetSupabaseDashboardURL()+"/project/"+project.Id))
fmt.Printf(
"Created a new project %s at %s\n",
utils.Aqua(resp.JSON201.Name),
utils.Aqua(utils.GetSupabaseDashboardURL()+"/project/"+resp.JSON201.Id),
)
return nil
}
Loading

0 comments on commit 5b86847

Please sign in to comment.