did some fixes

This commit is contained in:
anonpenguin23 2026-01-22 16:05:03 +02:00
parent c2071586f8
commit 0a7e3ba3c7
3 changed files with 184 additions and 2 deletions

View File

@ -32,6 +32,11 @@ func TestDomainRouting_BasicRouting(t *testing.T) {
// Wait for deployment to be active // Wait for deployment to be active
time.Sleep(2 * time.Second) time.Sleep(2 * time.Second)
// Get deployment details for debugging
deployment := GetDeployment(t, env, deploymentID)
t.Logf("Deployment created: ID=%s, CID=%s, Name=%s, Status=%s",
deploymentID, deployment["content_cid"], deployment["name"], deployment["status"])
t.Run("Standard domain resolves", func(t *testing.T) { t.Run("Standard domain resolves", func(t *testing.T) {
// Domain format: {deploymentName}.orama.network // Domain format: {deploymentName}.orama.network
domain := fmt.Sprintf("%s.orama.network", deploymentName) domain := fmt.Sprintf("%s.orama.network", deploymentName)

View File

@ -1,11 +1,14 @@
package deployments package deployments
import ( import (
"archive/tar"
"compress/gzip"
"context" "context"
"encoding/json" "encoding/json"
"fmt" "fmt"
"io" "io"
"net/http" "net/http"
"os"
"path/filepath" "path/filepath"
"strings" "strings"
"time" "time"
@ -88,8 +91,23 @@ func (h *StaticDeploymentHandler) HandleUpload(w http.ResponseWriter, r *http.Re
zap.Int64("size", header.Size), zap.Int64("size", header.Size),
) )
// Upload to IPFS // Extract tarball to temporary directory
addResp, err := h.ipfsClient.Add(ctx, file, header.Filename) tmpDir, err := os.MkdirTemp("", "static-deploy-*")
if err != nil {
h.logger.Error("Failed to create temp directory", zap.Error(err))
http.Error(w, "Failed to process tarball", http.StatusInternalServerError)
return
}
defer os.RemoveAll(tmpDir)
if err := extractTarball(file, tmpDir); err != nil {
h.logger.Error("Failed to extract tarball", zap.Error(err))
http.Error(w, "Failed to extract tarball", http.StatusInternalServerError)
return
}
// Upload extracted directory to IPFS
addResp, err := h.ipfsClient.AddDirectory(ctx, tmpDir)
if err != nil { if err != nil {
h.logger.Error("Failed to upload to IPFS", zap.Error(err)) h.logger.Error("Failed to upload to IPFS", zap.Error(err))
http.Error(w, "Failed to upload content", http.StatusInternalServerError) http.Error(w, "Failed to upload content", http.StatusInternalServerError)
@ -232,3 +250,61 @@ func detectContentType(filename string) string {
return "application/octet-stream" return "application/octet-stream"
} }
// extractTarball extracts a .tar.gz file to the specified directory
func extractTarball(reader io.Reader, destDir string) error {
gzr, err := gzip.NewReader(reader)
if err != nil {
return fmt.Errorf("failed to create gzip reader: %w", err)
}
defer gzr.Close()
tr := tar.NewReader(gzr)
for {
header, err := tr.Next()
if err == io.EOF {
break
}
if err != nil {
return fmt.Errorf("failed to read tar header: %w", err)
}
// Build target path
target := filepath.Join(destDir, header.Name)
// Prevent path traversal - clean both paths before comparing
cleanDest := filepath.Clean(destDir) + string(os.PathSeparator)
cleanTarget := filepath.Clean(target)
if !strings.HasPrefix(cleanTarget, cleanDest) && cleanTarget != filepath.Clean(destDir) {
return fmt.Errorf("invalid file path in tarball: %s", header.Name)
}
switch header.Typeflag {
case tar.TypeDir:
if err := os.MkdirAll(target, 0755); err != nil {
return fmt.Errorf("failed to create directory: %w", err)
}
case tar.TypeReg:
// Create parent directory if needed
if err := os.MkdirAll(filepath.Dir(target), 0755); err != nil {
return fmt.Errorf("failed to create parent directory: %w", err)
}
// Create file
f, err := os.OpenFile(target, os.O_CREATE|os.O_RDWR, os.FileMode(header.Mode))
if err != nil {
return fmt.Errorf("failed to create file: %w", err)
}
if _, err := io.Copy(f, tr); err != nil {
f.Close()
return fmt.Errorf("failed to write file: %w", err)
}
f.Close()
}
}
return nil
}

View File

@ -10,6 +10,8 @@ import (
"mime/multipart" "mime/multipart"
"net/http" "net/http"
"net/url" "net/url"
"os"
"path/filepath"
"strings" "strings"
"time" "time"
@ -19,6 +21,7 @@ import (
// IPFSClient defines the interface for IPFS operations // IPFSClient defines the interface for IPFS operations
type IPFSClient interface { type IPFSClient interface {
Add(ctx context.Context, reader io.Reader, name string) (*AddResponse, error) Add(ctx context.Context, reader io.Reader, name string) (*AddResponse, error)
AddDirectory(ctx context.Context, dirPath string) (*AddResponse, error)
Pin(ctx context.Context, cid string, name string, replicationFactor int) (*PinResponse, error) Pin(ctx context.Context, cid string, name string, replicationFactor int) (*PinResponse, error)
PinStatus(ctx context.Context, cid string) (*PinStatus, error) PinStatus(ctx context.Context, cid string) (*PinStatus, error)
Get(ctx context.Context, cid string, ipfsAPIURL string) (io.ReadCloser, error) Get(ctx context.Context, cid string, ipfsAPIURL string) (io.ReadCloser, error)
@ -236,6 +239,104 @@ func (c *Client) Add(ctx context.Context, reader io.Reader, name string) (*AddRe
return &last, nil return &last, nil
} }
// AddDirectory adds all files in a directory to IPFS and returns the root directory CID
func (c *Client) AddDirectory(ctx context.Context, dirPath string) (*AddResponse, error) {
var buf bytes.Buffer
writer := multipart.NewWriter(&buf)
// Walk directory and add all files to multipart request
var totalSize int64
err := filepath.Walk(dirPath, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
// Skip directories
if info.IsDir() {
return nil
}
// Get relative path
relPath, err := filepath.Rel(dirPath, path)
if err != nil {
return fmt.Errorf("failed to get relative path: %w", err)
}
// Read file
data, err := os.ReadFile(path)
if err != nil {
return fmt.Errorf("failed to read file %s: %w", path, err)
}
totalSize += int64(len(data))
// Add file to multipart
part, err := writer.CreateFormFile("file", relPath)
if err != nil {
return fmt.Errorf("failed to create form file: %w", err)
}
if _, err := part.Write(data); err != nil {
return fmt.Errorf("failed to write file data: %w", err)
}
return nil
})
if err != nil {
return nil, err
}
if err := writer.Close(); err != nil {
return nil, fmt.Errorf("failed to close writer: %w", err)
}
// Add with wrap-in-directory to create a root directory node
apiURL := c.apiURL + "/add?wrap-in-directory=true"
req, err := http.NewRequestWithContext(ctx, "POST", apiURL, &buf)
if err != nil {
return nil, fmt.Errorf("failed to create add request: %w", err)
}
req.Header.Set("Content-Type", writer.FormDataContentType())
resp, err := c.httpClient.Do(req)
if err != nil {
return nil, fmt.Errorf("add request failed: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(resp.Body)
return nil, fmt.Errorf("add failed with status %d: %s", resp.StatusCode, string(body))
}
// Read NDJSON responses - the last one will be the root directory
dec := json.NewDecoder(resp.Body)
var last AddResponse
for {
var chunk AddResponse
if err := dec.Decode(&chunk); err != nil {
if errors.Is(err, io.EOF) {
break
}
return nil, fmt.Errorf("failed to decode add response: %w", err)
}
last = chunk
}
if last.Cid == "" {
return nil, fmt.Errorf("no CID returned from IPFS")
}
return &AddResponse{
Cid: last.Cid,
Size: totalSize,
}, nil
}
// Pin pins a CID with specified replication factor // Pin pins a CID with specified replication factor
// IPFS Cluster expects pin options (including name) as query parameters, not in JSON body // IPFS Cluster expects pin options (including name) as query parameters, not in JSON body
func (c *Client) Pin(ctx context.Context, cid string, name string, replicationFactor int) (*PinResponse, error) { func (c *Client) Pin(ctx context.Context, cid string, name string, replicationFactor int) (*PinResponse, error) {