package create import ( "context" "fmt" "os" "strings" "github.com/go-errors/errors" "github.com/spf13/afero" "github.com/spf13/viper" "github.com/supabase/cli/internal/utils" "github.com/supabase/cli/internal/utils/credentials" "github.com/supabase/cli/internal/utils/flags" "github.com/supabase/cli/pkg/api" ) func Run(ctx context.Context, params api.V1CreateProjectBodyDto, fsys afero.Fs) error { if err := promptMissingParams(ctx, ¶ms); err != nil { return err } resp, err := utils.GetSupabase().V1CreateAProjectWithResponse(ctx, params) if err != nil { return errors.Errorf("failed to create project: %w", err) } if resp.JSON201 == nil { return errors.New("Unexpected error creating project: " + string(resp.Body)) } flags.ProjectRef = resp.JSON201.Id viper.Set("DB_PASSWORD", params.DbPass) if err := credentials.StoreProvider.Set(flags.ProjectRef, params.DbPass); err != nil { fmt.Fprintln(os.Stderr, "Failed to save database password:", err) } projectUrl := fmt.Sprintf("%s/project/%s", utils.GetSupabaseDashboardURL(), resp.JSON201.Id) fmt.Fprintf(os.Stderr, "Created a new project %s at %s\n", utils.Aqua(resp.JSON201.Name), utils.Bold(projectUrl)) if utils.OutputFormat.Value == utils.OutputPretty { return nil } return utils.EncodeOutput(utils.OutputFormat.Value, os.Stdout, resp.JSON201) } func printKeyValue(key, value string) string { indent := 20 - len(key) spaces := strings.Repeat(" ", indent) return key + ":" + spaces + value } func promptMissingParams(ctx context.Context, body *api.V1CreateProjectBodyDto) error { var err error if len(body.Name) == 0 { if body.Name, err = promptProjectName(ctx); err != nil { return err } } else { fmt.Fprintln(os.Stderr, printKeyValue("Creating project", body.Name)) } if len(body.OrganizationId) == 0 { if body.OrganizationId, err = promptOrgId(ctx); err != nil { return err } fmt.Fprintln(os.Stderr, printKeyValue("Selected org-id", body.OrganizationId)) } if len(body.Region) == 0 { if body.Region, err = promptProjectRegion(ctx); err != nil { return err } fmt.Fprintln(os.Stderr, printKeyValue("Selected region", string(body.Region))) } if len(body.DbPass) == 0 { body.DbPass = flags.PromptPassword(os.Stdin) } return nil } func promptProjectName(ctx context.Context) (string, error) { title := "Enter your project name: " if name, err := utils.NewConsole().PromptText(ctx, title); err != nil { return "", err } else if len(name) > 0 { return name, nil } return "", errors.New("project name cannot be empty") } func promptOrgId(ctx context.Context) (string, error) { title := "Which organisation do you want to create the project for?" resp, err := utils.GetSupabase().V1ListAllOrganizationsWithResponse(ctx) if err != nil { return "", err } if resp.JSON200 == nil { return "", errors.New("Unexpected error retrieving organizations: " + string(resp.Body)) } 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 } return choice.Details, nil } func promptProjectRegion(ctx context.Context) (api.V1CreateProjectBodyDtoRegion, error) { 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 } return api.V1CreateProjectBodyDtoRegion(choice.Summary), nil }