package app import ( "crypto/rand" "encoding/json" "fmt" "net/http" "net/url" "os/exec" "regexp" "strconv" "strings" ) func validateEnvKey(key string) error { if key == "" { return fmt.Errorf("key is required") } if !regexp.MustCompile(`^[A-Za-z_][A-Za-z0-9_]*$`).MatchString(key) { return fmt.Errorf("invalid env key format") } return nil } func isValidRepoURL(s string) bool { return strings.HasPrefix(s, "https://") || strings.HasPrefix(s, "http://") || strings.HasPrefix(s, "ssh://") || strings.HasPrefix(s, "git@") } func escapeEnvValue(v string) string { return strconv.Quote(v) } func slugify(s string) string { s = strings.ToLower(strings.TrimSpace(s)) s = regexp.MustCompile(`[^a-z0-9]+`).ReplaceAllString(s, "-") return strings.Trim(s, "-") } func writeJSON(w http.ResponseWriter, status int, data any) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(status) _ = json.NewEncoder(w).Encode(data) } func mustJSONPretty(data any) string { b, err := json.MarshalIndent(data, "", " ") if err != nil { return "{}" } return string(b) } func (a *App) deployWebhookURL(projectID int64, token, userToken string) string { if token == "" || userToken == "" { return "" } path := fmt.Sprintf("/api/webhooks/deploy/%d/%s?ut=%s", projectID, token, url.QueryEscape(userToken)) if a.cfg.WebhookBaseURL != "" { return a.cfg.WebhookBaseURL + path } return path } func (a *App) appURL(routeHost string) string { routeHost = strings.TrimSpace(routeHost) if routeHost == "" { return "" } return "https://" + routeHost } func newDeployToken() (string, error) { buf := make([]byte, 24) if _, err := rand.Read(buf); err != nil { return "", err } return fmt.Sprintf("%x", buf), nil } func runCmd(name string, args ...string) error { _, err := runCmdOut(name, args...) return err } func runCmdOut(name string, args ...string) (string, error) { out, err := exec.Command(name, args...).CombinedOutput() if err != nil { return "", fmt.Errorf("command failed: %s %s: %w output=%s", name, strings.Join(args, " "), err, strings.TrimSpace(string(out))) } return string(out), nil }