mirror of
https://github.com/DeBrosOfficial/network.git
synced 2026-01-30 14:33:03 +00:00
Fixed IPFS systemd service and deploy issue on nextjs
This commit is contained in:
parent
4b24b0aa6c
commit
571f8babb4
@ -10,6 +10,7 @@ import (
|
|||||||
"mime/multipart"
|
"mime/multipart"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@ -94,6 +95,14 @@ func init() {
|
|||||||
func deployStatic(cmd *cobra.Command, args []string) error {
|
func deployStatic(cmd *cobra.Command, args []string) error {
|
||||||
sourcePath := args[0]
|
sourcePath := args[0]
|
||||||
|
|
||||||
|
// Warn if source looks like it needs building
|
||||||
|
if _, err := os.Stat(filepath.Join(sourcePath, "package.json")); err == nil {
|
||||||
|
if _, err := os.Stat(filepath.Join(sourcePath, "index.html")); os.IsNotExist(err) {
|
||||||
|
fmt.Printf("⚠️ Warning: %s has package.json but no index.html. You may need to build first.\n", sourcePath)
|
||||||
|
fmt.Printf(" Try: cd %s && npm run build, then deploy the output directory (e.g. dist/ or out/)\n\n", sourcePath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fmt.Printf("📦 Creating tarball from %s...\n", sourcePath)
|
fmt.Printf("📦 Creating tarball from %s...\n", sourcePath)
|
||||||
tarball, err := createTarball(sourcePath)
|
tarball, err := createTarball(sourcePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -123,10 +132,67 @@ func deployStatic(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func deployNextJS(cmd *cobra.Command, args []string) error {
|
func deployNextJS(cmd *cobra.Command, args []string) error {
|
||||||
sourcePath := args[0]
|
sourcePath, err := filepath.Abs(args[0])
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to resolve path: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
fmt.Printf("📦 Creating tarball from %s...\n", sourcePath)
|
// Verify it's a Next.js project
|
||||||
tarball, err := createTarball(sourcePath)
|
if _, err := os.Stat(filepath.Join(sourcePath, "package.json")); os.IsNotExist(err) {
|
||||||
|
return fmt.Errorf("no package.json found in %s", sourcePath)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 1: Install dependencies if needed
|
||||||
|
if _, err := os.Stat(filepath.Join(sourcePath, "node_modules")); os.IsNotExist(err) {
|
||||||
|
fmt.Printf("📦 Installing dependencies...\n")
|
||||||
|
if err := runBuildCommand(sourcePath, "npm", "install"); err != nil {
|
||||||
|
return fmt.Errorf("npm install failed: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 2: Build
|
||||||
|
fmt.Printf("🔨 Building Next.js application...\n")
|
||||||
|
if err := runBuildCommand(sourcePath, "npm", "run", "build"); err != nil {
|
||||||
|
return fmt.Errorf("build failed: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var tarball string
|
||||||
|
if deploySSR {
|
||||||
|
// SSR: tarball the standalone output
|
||||||
|
standalonePath := filepath.Join(sourcePath, ".next", "standalone")
|
||||||
|
if _, err := os.Stat(standalonePath); os.IsNotExist(err) {
|
||||||
|
return fmt.Errorf(".next/standalone/ not found. Ensure next.config.js has output: 'standalone'")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy static assets into standalone
|
||||||
|
staticSrc := filepath.Join(sourcePath, ".next", "static")
|
||||||
|
staticDst := filepath.Join(standalonePath, ".next", "static")
|
||||||
|
if _, err := os.Stat(staticSrc); err == nil {
|
||||||
|
if err := copyDir(staticSrc, staticDst); err != nil {
|
||||||
|
return fmt.Errorf("failed to copy static assets: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy public directory if it exists
|
||||||
|
publicSrc := filepath.Join(sourcePath, "public")
|
||||||
|
publicDst := filepath.Join(standalonePath, "public")
|
||||||
|
if _, err := os.Stat(publicSrc); err == nil {
|
||||||
|
if err := copyDir(publicSrc, publicDst); err != nil {
|
||||||
|
return fmt.Errorf("failed to copy public directory: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("📦 Creating tarball from standalone output...\n")
|
||||||
|
tarball, err = createTarballAll(standalonePath)
|
||||||
|
} else {
|
||||||
|
// Static export: tarball the out/ directory
|
||||||
|
outPath := filepath.Join(sourcePath, "out")
|
||||||
|
if _, err := os.Stat(outPath); os.IsNotExist(err) {
|
||||||
|
return fmt.Errorf("out/ directory not found. For static export, ensure next.config.js has output: 'export'")
|
||||||
|
}
|
||||||
|
fmt.Printf("📦 Creating tarball from static export...\n")
|
||||||
|
tarball, err = createTarball(outPath)
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to create tarball: %w", err)
|
return fmt.Errorf("failed to create tarball: %w", err)
|
||||||
}
|
}
|
||||||
@ -159,10 +225,30 @@ func deployNextJS(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func deployGo(cmd *cobra.Command, args []string) error {
|
func deployGo(cmd *cobra.Command, args []string) error {
|
||||||
sourcePath := args[0]
|
sourcePath, err := filepath.Abs(args[0])
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to resolve path: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
fmt.Printf("📦 Creating tarball from %s...\n", sourcePath)
|
// Verify it's a Go project
|
||||||
tarball, err := createTarball(sourcePath)
|
if _, err := os.Stat(filepath.Join(sourcePath, "go.mod")); os.IsNotExist(err) {
|
||||||
|
return fmt.Errorf("no go.mod found in %s", sourcePath)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cross-compile for Linux amd64 (production VPS target)
|
||||||
|
fmt.Printf("🔨 Building Go binary (linux/amd64)...\n")
|
||||||
|
buildCmd := exec.Command("go", "build", "-o", "app", ".")
|
||||||
|
buildCmd.Dir = sourcePath
|
||||||
|
buildCmd.Env = append(os.Environ(), "GOOS=linux", "GOARCH=amd64", "CGO_ENABLED=0")
|
||||||
|
buildCmd.Stdout = os.Stdout
|
||||||
|
buildCmd.Stderr = os.Stderr
|
||||||
|
if err := buildCmd.Run(); err != nil {
|
||||||
|
return fmt.Errorf("go build failed: %w", err)
|
||||||
|
}
|
||||||
|
defer os.Remove(filepath.Join(sourcePath, "app")) // Clean up after tarball
|
||||||
|
|
||||||
|
fmt.Printf("📦 Creating tarball...\n")
|
||||||
|
tarball, err := createTarballFiles(sourcePath, []string{"app"})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to create tarball: %w", err)
|
return fmt.Errorf("failed to create tarball: %w", err)
|
||||||
}
|
}
|
||||||
@ -190,9 +276,33 @@ func deployGo(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func deployNodeJS(cmd *cobra.Command, args []string) error {
|
func deployNodeJS(cmd *cobra.Command, args []string) error {
|
||||||
sourcePath := args[0]
|
sourcePath, err := filepath.Abs(args[0])
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to resolve path: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
fmt.Printf("📦 Creating tarball from %s...\n", sourcePath)
|
// Verify it's a Node.js project
|
||||||
|
if _, err := os.Stat(filepath.Join(sourcePath, "package.json")); os.IsNotExist(err) {
|
||||||
|
return fmt.Errorf("no package.json found in %s", sourcePath)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Install dependencies if needed
|
||||||
|
if _, err := os.Stat(filepath.Join(sourcePath, "node_modules")); os.IsNotExist(err) {
|
||||||
|
fmt.Printf("📦 Installing dependencies...\n")
|
||||||
|
if err := runBuildCommand(sourcePath, "npm", "install", "--production"); err != nil {
|
||||||
|
return fmt.Errorf("npm install failed: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run build script if it exists
|
||||||
|
if hasBuildScript(sourcePath) {
|
||||||
|
fmt.Printf("🔨 Building...\n")
|
||||||
|
if err := runBuildCommand(sourcePath, "npm", "run", "build"); err != nil {
|
||||||
|
return fmt.Errorf("build failed: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("📦 Creating tarball...\n")
|
||||||
tarball, err := createTarball(sourcePath)
|
tarball, err := createTarball(sourcePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to create tarball: %w", err)
|
return fmt.Errorf("failed to create tarball: %w", err)
|
||||||
@ -220,7 +330,115 @@ func deployNodeJS(cmd *cobra.Command, args []string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// runBuildCommand runs a command in the given directory with stdout/stderr streaming
|
||||||
|
func runBuildCommand(dir string, name string, args ...string) error {
|
||||||
|
cmd := exec.Command(name, args...)
|
||||||
|
cmd.Dir = dir
|
||||||
|
cmd.Stdout = os.Stdout
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
return cmd.Run()
|
||||||
|
}
|
||||||
|
|
||||||
|
// hasBuildScript checks if package.json has a "build" script
|
||||||
|
func hasBuildScript(dir string) bool {
|
||||||
|
data, err := os.ReadFile(filepath.Join(dir, "package.json"))
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
var pkg map[string]interface{}
|
||||||
|
if err := json.Unmarshal(data, &pkg); err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
scripts, ok := pkg["scripts"].(map[string]interface{})
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
_, ok = scripts["build"]
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
// copyDir recursively copies a directory
|
||||||
|
func copyDir(src, dst string) error {
|
||||||
|
return filepath.Walk(src, func(path string, info os.FileInfo, err error) error {
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
relPath, err := filepath.Rel(src, path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
dstPath := filepath.Join(dst, relPath)
|
||||||
|
|
||||||
|
if info.IsDir() {
|
||||||
|
return os.MkdirAll(dstPath, info.Mode())
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := os.ReadFile(path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return os.WriteFile(dstPath, data, info.Mode())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// createTarballFiles creates a tarball containing only specific files from a directory
|
||||||
|
func createTarballFiles(baseDir string, files []string) (string, error) {
|
||||||
|
tmpFile, err := os.CreateTemp("", "orama-deploy-*.tar.gz")
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
defer tmpFile.Close()
|
||||||
|
|
||||||
|
gzWriter := gzip.NewWriter(tmpFile)
|
||||||
|
defer gzWriter.Close()
|
||||||
|
|
||||||
|
tarWriter := tar.NewWriter(gzWriter)
|
||||||
|
defer tarWriter.Close()
|
||||||
|
|
||||||
|
for _, f := range files {
|
||||||
|
fullPath := filepath.Join(baseDir, f)
|
||||||
|
info, err := os.Stat(fullPath)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("file %s not found: %w", f, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
header, err := tar.FileInfoHeader(info, "")
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
header.Name = f
|
||||||
|
|
||||||
|
if err := tarWriter.WriteHeader(header); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !info.IsDir() {
|
||||||
|
file, err := os.Open(fullPath)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
_, err = io.Copy(tarWriter, file)
|
||||||
|
file.Close()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return tmpFile.Name(), nil
|
||||||
|
}
|
||||||
|
|
||||||
func createTarball(sourcePath string) (string, error) {
|
func createTarball(sourcePath string) (string, error) {
|
||||||
|
return createTarballWithOptions(sourcePath, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// createTarballAll creates a tarball including node_modules and hidden dirs (for standalone output)
|
||||||
|
func createTarballAll(sourcePath string) (string, error) {
|
||||||
|
return createTarballWithOptions(sourcePath, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func createTarballWithOptions(sourcePath string, skipNodeModules bool) (string, error) {
|
||||||
// Create temp file
|
// Create temp file
|
||||||
tmpFile, err := os.CreateTemp("", "orama-deploy-*.tar.gz")
|
tmpFile, err := os.CreateTemp("", "orama-deploy-*.tar.gz")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -242,15 +460,17 @@ func createTarball(sourcePath string) (string, error) {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Skip hidden files and node_modules
|
// Skip hidden files and node_modules (unless disabled)
|
||||||
if strings.HasPrefix(info.Name(), ".") && info.Name() != "." {
|
if skipNodeModules {
|
||||||
if info.IsDir() {
|
if strings.HasPrefix(info.Name(), ".") && info.Name() != "." {
|
||||||
|
if info.IsDir() {
|
||||||
|
return filepath.SkipDir
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if info.Name() == "node_modules" {
|
||||||
return filepath.SkipDir
|
return filepath.SkipDir
|
||||||
}
|
}
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if info.Name() == "node_modules" {
|
|
||||||
return filepath.SkipDir
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create tar header
|
// Create tar header
|
||||||
|
|||||||
@ -395,8 +395,8 @@ func (m *Manager) getStartCommand(deployment *deployments.Deployment, workDir st
|
|||||||
|
|
||||||
switch deployment.Type {
|
switch deployment.Type {
|
||||||
case deployments.DeploymentTypeNextJS:
|
case deployments.DeploymentTypeNextJS:
|
||||||
// Next.js standalone output places server at .next/standalone/server.js
|
// CLI tarballs the standalone output directly, so server.js is at the root
|
||||||
return nodePath + " .next/standalone/server.js"
|
return nodePath + " server.js"
|
||||||
case deployments.DeploymentTypeNodeJSBackend:
|
case deployments.DeploymentTypeNodeJSBackend:
|
||||||
// Check if ENTRY_POINT is set in environment
|
// Check if ENTRY_POINT is set in environment
|
||||||
if entryPoint, ok := deployment.Environment["ENTRY_POINT"]; ok {
|
if entryPoint, ok := deployment.Environment["ENTRY_POINT"]; ok {
|
||||||
|
|||||||
@ -89,6 +89,7 @@ Environment=HOME=%[1]s
|
|||||||
Environment=IPFS_CLUSTER_PATH=%[2]s
|
Environment=IPFS_CLUSTER_PATH=%[2]s
|
||||||
Environment=CLUSTER_SECRET=%[5]s
|
Environment=CLUSTER_SECRET=%[5]s
|
||||||
ExecStartPre=/bin/bash -c 'mkdir -p %[2]s && chmod 700 %[2]s'
|
ExecStartPre=/bin/bash -c 'mkdir -p %[2]s && chmod 700 %[2]s'
|
||||||
|
ExecStartPre=/bin/bash -c 'for i in $(seq 1 30); do curl -sf -X POST http://127.0.0.1:4501/api/v0/id > /dev/null 2>&1 && exit 0; sleep 1; done; echo "IPFS API not ready after 30s"; exit 1'
|
||||||
ExecStart=%[4]s daemon
|
ExecStart=%[4]s daemon
|
||||||
Restart=always
|
Restart=always
|
||||||
RestartSec=5
|
RestartSec=5
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user