package parsecli import ( "errors" "fmt" "net/url" "os" "path/filepath" "regexp" "strings" "github.com/facebookgo/errgroup" "github.com/facebookgo/parse" "github.com/facebookgo/stackerr" ) const ( SampleSource = ` // Use Parse.Cloud.define to define as many cloud functions as you want. // For example: Parse.Cloud.define("hello", function(request, response) { response.success("Hello world!"); }); ` SampleHTML = ` My ParseApp site

Congratulations! You're already hosting with Parse.

To get started, edit this file at public/index.html and start adding static content.

If you want something a bit more dynamic, delete this file and check out our hosting docs.

` ) func getHostFromURL(urlStr, email string) (string, error) { netURL, err := url.Parse(urlStr) if err != nil { return "", stackerr.Wrap(err) } server := regexp.MustCompile(`(.*):\d+$`).ReplaceAllString(netURL.Host, "$1") if server == "" { return "", stackerr.Newf("%s is not a valid url", urlStr) } if email != "" { return fmt.Sprintf("%s#%s", server, email), nil } return server, nil } func Last4(str string) string { l := len(str) if l > 4 { return fmt.Sprintf("%s%s", strings.Repeat("*", l-4), str[l-4:l]) } return str } // errorString returns the error string with our without the stack trace // depending on the Environment variable. this exists because we want plain // messages for end users, but when we're working on the CLI we want the stack // trace for debugging. func ErrorString(e *Env, err error) string { type hasUnderlying interface { HasUnderlying() error } parseErr := func(err error) error { if apiErr, ok := err.(*parse.Error); ok { return errors.New(apiErr.Message) } return err } lastErr := func(err error) error { if serr, ok := err.(*stackerr.Error); ok { if errs := stackerr.Underlying(serr); len(errs) != 0 { err = errs[len(errs)-1] } } else { if eu, ok := err.(hasUnderlying); ok { err = eu.HasUnderlying() } } return parseErr(err) } if !e.ErrorStack { if merr, ok := err.(errgroup.MultiError); ok { var multiError []error for _, ierr := range []error(merr) { multiError = append(multiError, lastErr(ierr)) } err = errgroup.MultiError(multiError) } else { err = lastErr(err) } return parseErr(err).Error() } return err.Error() } func CreateConfigWithContent(path, content string) error { file, err := os.OpenFile( path, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600, ) if err != nil && !os.IsExist(err) { return stackerr.Wrap(err) } defer file.Close() if _, err := file.WriteString(content); err != nil { return stackerr.Wrap(err) } if err := file.Close(); err != nil { return stackerr.Wrap(err) } return nil } var NewProjectFiles = []struct { Dirname, Filename, Content string }{ {"cloud", "main.js", SampleSource}, {"public", "index.html", SampleHTML}, } func CloneSampleCloudCode(e *Env, dumpTemplate bool) error { err := os.MkdirAll(e.Root, 0755) if err != nil { return stackerr.Wrap(err) } err = CreateConfigWithContent( filepath.Join(e.Root, ParseProject), fmt.Sprintf( `{ "project_type" : %d, "parse": {"jssdk":""} }`, ParseFormat, ), ) if err != nil { return err } err = CreateConfigWithContent( filepath.Join(e.Root, ParseLocal), "{}", ) if err != nil { return err } // no need to set up the template code if !dumpTemplate { return nil } for _, info := range NewProjectFiles { sampleDir := filepath.Join(e.Root, info.Dirname) if _, err := os.Stat(sampleDir); err != nil { if !os.IsNotExist(err) { return stackerr.Wrap(err) } if err := os.Mkdir(sampleDir, 0755); err != nil { return stackerr.Wrap(err) } } sampleFile := filepath.Join(sampleDir, info.Filename) if _, err := os.Stat(sampleFile); err != nil { if os.IsNotExist(err) { file, err := os.Create(sampleFile) if err != nil && !os.IsExist(err) { return stackerr.Wrap(err) } defer file.Close() if _, err := file.WriteString(info.Content); err != nil { return stackerr.Wrap(err) } if err := file.Close(); err != nil { return stackerr.Wrap(err) } } } } return nil }