mirror of
https://github.com/DeBrosOfficial/network.git
synced 2026-01-30 19:23:03 +00:00
419 lines
10 KiB
Go
419 lines
10 KiB
Go
package deployments
|
|
|
|
import (
|
|
"archive/tar"
|
|
"bytes"
|
|
"compress/gzip"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"mime/multipart"
|
|
"net/http"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"github.com/DeBrosOfficial/network/pkg/auth"
|
|
"github.com/spf13/cobra"
|
|
)
|
|
|
|
// DeployCmd is the root deploy command
|
|
var DeployCmd = &cobra.Command{
|
|
Use: "deploy",
|
|
Short: "Deploy applications",
|
|
Long: "Deploy static sites, Next.js apps, Go backends, and Node.js backends",
|
|
}
|
|
|
|
// DeployStaticCmd deploys a static site
|
|
var DeployStaticCmd = &cobra.Command{
|
|
Use: "static <source_path>",
|
|
Short: "Deploy a static site (React, Vue, etc.)",
|
|
Args: cobra.ExactArgs(1),
|
|
RunE: deployStatic,
|
|
}
|
|
|
|
// DeployNextJSCmd deploys a Next.js application
|
|
var DeployNextJSCmd = &cobra.Command{
|
|
Use: "nextjs <source_path>",
|
|
Short: "Deploy a Next.js application",
|
|
Args: cobra.ExactArgs(1),
|
|
RunE: deployNextJS,
|
|
}
|
|
|
|
// DeployGoCmd deploys a Go backend
|
|
var DeployGoCmd = &cobra.Command{
|
|
Use: "go <source_path>",
|
|
Short: "Deploy a Go backend",
|
|
Args: cobra.ExactArgs(1),
|
|
RunE: deployGo,
|
|
}
|
|
|
|
// DeployNodeJSCmd deploys a Node.js backend
|
|
var DeployNodeJSCmd = &cobra.Command{
|
|
Use: "nodejs <source_path>",
|
|
Short: "Deploy a Node.js backend",
|
|
Args: cobra.ExactArgs(1),
|
|
RunE: deployNodeJS,
|
|
}
|
|
|
|
var (
|
|
deployName string
|
|
deploySubdomain string
|
|
deploySSR bool
|
|
deployUpdate bool
|
|
)
|
|
|
|
func init() {
|
|
DeployStaticCmd.Flags().StringVar(&deployName, "name", "", "Deployment name (required)")
|
|
DeployStaticCmd.Flags().StringVar(&deploySubdomain, "subdomain", "", "Custom subdomain")
|
|
DeployStaticCmd.Flags().BoolVar(&deployUpdate, "update", false, "Update existing deployment")
|
|
DeployStaticCmd.MarkFlagRequired("name")
|
|
|
|
DeployNextJSCmd.Flags().StringVar(&deployName, "name", "", "Deployment name (required)")
|
|
DeployNextJSCmd.Flags().StringVar(&deploySubdomain, "subdomain", "", "Custom subdomain")
|
|
DeployNextJSCmd.Flags().BoolVar(&deploySSR, "ssr", false, "Deploy with SSR (server-side rendering)")
|
|
DeployNextJSCmd.Flags().BoolVar(&deployUpdate, "update", false, "Update existing deployment")
|
|
DeployNextJSCmd.MarkFlagRequired("name")
|
|
|
|
DeployGoCmd.Flags().StringVar(&deployName, "name", "", "Deployment name (required)")
|
|
DeployGoCmd.Flags().StringVar(&deploySubdomain, "subdomain", "", "Custom subdomain")
|
|
DeployGoCmd.Flags().BoolVar(&deployUpdate, "update", false, "Update existing deployment")
|
|
DeployGoCmd.MarkFlagRequired("name")
|
|
|
|
DeployNodeJSCmd.Flags().StringVar(&deployName, "name", "", "Deployment name (required)")
|
|
DeployNodeJSCmd.Flags().StringVar(&deploySubdomain, "subdomain", "", "Custom subdomain")
|
|
DeployNodeJSCmd.Flags().BoolVar(&deployUpdate, "update", false, "Update existing deployment")
|
|
DeployNodeJSCmd.MarkFlagRequired("name")
|
|
|
|
DeployCmd.AddCommand(DeployStaticCmd)
|
|
DeployCmd.AddCommand(DeployNextJSCmd)
|
|
DeployCmd.AddCommand(DeployGoCmd)
|
|
DeployCmd.AddCommand(DeployNodeJSCmd)
|
|
}
|
|
|
|
func deployStatic(cmd *cobra.Command, args []string) error {
|
|
sourcePath := args[0]
|
|
|
|
fmt.Printf("📦 Creating tarball from %s...\n", sourcePath)
|
|
tarball, err := createTarball(sourcePath)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to create tarball: %w", err)
|
|
}
|
|
defer os.Remove(tarball)
|
|
|
|
fmt.Printf("☁️ Uploading to Orama Network...\n")
|
|
|
|
endpoint := "/v1/deployments/static/upload"
|
|
if deployUpdate {
|
|
endpoint = "/v1/deployments/static/update"
|
|
}
|
|
|
|
resp, err := uploadDeployment(endpoint, tarball, map[string]string{
|
|
"name": deployName,
|
|
"subdomain": deploySubdomain,
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
fmt.Printf("\n✅ Deployment successful!\n\n")
|
|
printDeploymentInfo(resp)
|
|
|
|
return nil
|
|
}
|
|
|
|
func deployNextJS(cmd *cobra.Command, args []string) error {
|
|
sourcePath := args[0]
|
|
|
|
fmt.Printf("📦 Creating tarball from %s...\n", sourcePath)
|
|
tarball, err := createTarball(sourcePath)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to create tarball: %w", err)
|
|
}
|
|
defer os.Remove(tarball)
|
|
|
|
fmt.Printf("☁️ Uploading to Orama Network...\n")
|
|
|
|
endpoint := "/v1/deployments/nextjs/upload"
|
|
if deployUpdate {
|
|
endpoint = "/v1/deployments/nextjs/update"
|
|
}
|
|
|
|
resp, err := uploadDeployment(endpoint, tarball, map[string]string{
|
|
"name": deployName,
|
|
"subdomain": deploySubdomain,
|
|
"ssr": fmt.Sprintf("%t", deploySSR),
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
fmt.Printf("\n✅ Deployment successful!\n\n")
|
|
printDeploymentInfo(resp)
|
|
|
|
if deploySSR {
|
|
fmt.Printf("⚠️ Note: SSR deployment may take a minute to start. Check status with: orama deployments get %s\n", deployName)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func deployGo(cmd *cobra.Command, args []string) error {
|
|
sourcePath := args[0]
|
|
|
|
fmt.Printf("📦 Creating tarball from %s...\n", sourcePath)
|
|
tarball, err := createTarball(sourcePath)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to create tarball: %w", err)
|
|
}
|
|
defer os.Remove(tarball)
|
|
|
|
fmt.Printf("☁️ Uploading to Orama Network...\n")
|
|
|
|
endpoint := "/v1/deployments/go/upload"
|
|
if deployUpdate {
|
|
endpoint = "/v1/deployments/go/update"
|
|
}
|
|
|
|
resp, err := uploadDeployment(endpoint, tarball, map[string]string{
|
|
"name": deployName,
|
|
"subdomain": deploySubdomain,
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
fmt.Printf("\n✅ Deployment successful!\n\n")
|
|
printDeploymentInfo(resp)
|
|
|
|
return nil
|
|
}
|
|
|
|
func deployNodeJS(cmd *cobra.Command, args []string) error {
|
|
sourcePath := args[0]
|
|
|
|
fmt.Printf("📦 Creating tarball from %s...\n", sourcePath)
|
|
tarball, err := createTarball(sourcePath)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to create tarball: %w", err)
|
|
}
|
|
defer os.Remove(tarball)
|
|
|
|
fmt.Printf("☁️ Uploading to Orama Network...\n")
|
|
|
|
endpoint := "/v1/deployments/nodejs/upload"
|
|
if deployUpdate {
|
|
endpoint = "/v1/deployments/nodejs/update"
|
|
}
|
|
|
|
resp, err := uploadDeployment(endpoint, tarball, map[string]string{
|
|
"name": deployName,
|
|
"subdomain": deploySubdomain,
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
fmt.Printf("\n✅ Deployment successful!\n\n")
|
|
printDeploymentInfo(resp)
|
|
|
|
return nil
|
|
}
|
|
|
|
func createTarball(sourcePath string) (string, error) {
|
|
// Create temp file
|
|
tmpFile, err := os.CreateTemp("", "orama-deploy-*.tar.gz")
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
defer tmpFile.Close()
|
|
|
|
// Create gzip writer
|
|
gzWriter := gzip.NewWriter(tmpFile)
|
|
defer gzWriter.Close()
|
|
|
|
// Create tar writer
|
|
tarWriter := tar.NewWriter(gzWriter)
|
|
defer tarWriter.Close()
|
|
|
|
// Walk directory and add files
|
|
err = filepath.Walk(sourcePath, func(path string, info os.FileInfo, err error) error {
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Skip hidden files and node_modules
|
|
if strings.HasPrefix(info.Name(), ".") && info.Name() != "." {
|
|
if info.IsDir() {
|
|
return filepath.SkipDir
|
|
}
|
|
return nil
|
|
}
|
|
if info.Name() == "node_modules" {
|
|
return filepath.SkipDir
|
|
}
|
|
|
|
// Create tar header
|
|
header, err := tar.FileInfoHeader(info, "")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Update header name to be relative to source
|
|
relPath, err := filepath.Rel(sourcePath, path)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
header.Name = relPath
|
|
|
|
// Write header
|
|
if err := tarWriter.WriteHeader(header); err != nil {
|
|
return err
|
|
}
|
|
|
|
// Write file content if not a directory
|
|
if !info.IsDir() {
|
|
file, err := os.Open(path)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer file.Close()
|
|
|
|
_, err = io.Copy(tarWriter, file)
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
})
|
|
|
|
return tmpFile.Name(), err
|
|
}
|
|
|
|
func uploadDeployment(endpoint, tarballPath string, formData map[string]string) (map[string]interface{}, error) {
|
|
// Open tarball
|
|
file, err := os.Open(tarballPath)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer file.Close()
|
|
|
|
// Create multipart request
|
|
body := &bytes.Buffer{}
|
|
writer := multipart.NewWriter(body)
|
|
|
|
// Add form fields
|
|
for key, value := range formData {
|
|
writer.WriteField(key, value)
|
|
}
|
|
|
|
// Add file
|
|
part, err := writer.CreateFormFile("tarball", filepath.Base(tarballPath))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
_, err = io.Copy(part, file)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
writer.Close()
|
|
|
|
// Get API URL from config
|
|
apiURL := getAPIURL()
|
|
url := apiURL + endpoint
|
|
|
|
// Create request
|
|
req, err := http.NewRequest("POST", url, body)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
req.Header.Set("Content-Type", writer.FormDataContentType())
|
|
|
|
// Add auth header
|
|
token, err := getAuthToken()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("authentication required: %w", err)
|
|
}
|
|
req.Header.Set("Authorization", "Bearer "+token)
|
|
|
|
// Send request
|
|
client := &http.Client{}
|
|
resp, err := client.Do(req)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
// Read response
|
|
respBody, err := io.ReadAll(resp.Body)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if resp.StatusCode != http.StatusCreated && resp.StatusCode != http.StatusOK {
|
|
return nil, fmt.Errorf("deployment failed: %s", string(respBody))
|
|
}
|
|
|
|
// Parse response
|
|
var result map[string]interface{}
|
|
err = json.Unmarshal(respBody, &result)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return result, nil
|
|
}
|
|
|
|
func printDeploymentInfo(resp map[string]interface{}) {
|
|
fmt.Printf("Name: %s\n", resp["name"])
|
|
fmt.Printf("Type: %s\n", resp["type"])
|
|
fmt.Printf("Status: %s\n", resp["status"])
|
|
fmt.Printf("Version: %v\n", resp["version"])
|
|
if contentCID, ok := resp["content_cid"]; ok && contentCID != "" {
|
|
fmt.Printf("Content CID: %s\n", contentCID)
|
|
}
|
|
|
|
if urls, ok := resp["urls"].([]interface{}); ok && len(urls) > 0 {
|
|
fmt.Printf("\nURLs:\n")
|
|
for _, url := range urls {
|
|
fmt.Printf(" • %s\n", url)
|
|
}
|
|
}
|
|
}
|
|
|
|
func getAPIURL() string {
|
|
// Check environment variable first
|
|
if url := os.Getenv("ORAMA_API_URL"); url != "" {
|
|
return url
|
|
}
|
|
// Get from active environment config
|
|
return auth.GetDefaultGatewayURL()
|
|
}
|
|
|
|
func getAuthToken() (string, error) {
|
|
// Check environment variable first
|
|
if token := os.Getenv("ORAMA_TOKEN"); token != "" {
|
|
return token, nil
|
|
}
|
|
|
|
// Try to get from enhanced credentials store
|
|
store, err := auth.LoadEnhancedCredentials()
|
|
if err != nil {
|
|
return "", fmt.Errorf("failed to load credentials: %w", err)
|
|
}
|
|
|
|
gatewayURL := auth.GetDefaultGatewayURL()
|
|
creds := store.GetDefaultCredential(gatewayURL)
|
|
if creds == nil {
|
|
return "", fmt.Errorf("no credentials found for %s. Run 'orama auth login' to authenticate", gatewayURL)
|
|
}
|
|
|
|
if !creds.IsValid() {
|
|
return "", fmt.Errorf("credentials expired for %s. Run 'orama auth login' to re-authenticate", gatewayURL)
|
|
}
|
|
|
|
return creds.APIKey, nil
|
|
}
|