package docker import ( "context" "fmt" "strconv" "strings" "github.com/moby/moby/client" ) // SiteVolumeName returns the deterministic volume name for a static site. func SiteVolumeName(siteName string) string { return fmt.Sprintf("tinyforge-site-%s-data", siteName) } // EnsureSiteVolume creates a named volume for a static site if it doesn't already exist. // Returns the volume name. func (c *Client) EnsureSiteVolume(ctx context.Context, siteName string) (string, error) { name := SiteVolumeName(siteName) // Check if volume already exists. _, err := c.api.VolumeInspect(ctx, name, client.VolumeInspectOptions{}) if err == nil { return name, nil } // Create the volume. _, err = c.api.VolumeCreate(ctx, client.VolumeCreateOptions{ Name: name, Labels: map[string]string{ "tinyforge.static-site": "true", "tinyforge.site-name": siteName, }, }) if err != nil { return "", fmt.Errorf("create volume %s: %w", name, err) } return name, nil } // RemoveSiteVolume removes the named volume for a static site. func (c *Client) RemoveSiteVolume(ctx context.Context, siteName string) error { name := SiteVolumeName(siteName) _, err := c.api.VolumeRemove(ctx, name, client.VolumeRemoveOptions{Force: true}) if err != nil { return fmt.Errorf("remove volume %s: %w", name, err) } return nil } // StorageUsage holds the disk usage info for a site volume. type StorageUsage struct { UsedBytes int64 `json:"used_bytes"` } // InspectSiteStorageUsage execs `du -sb /app/data` inside the container to get usage. // Returns 0 if the container is not running or the path doesn't exist. func (c *Client) InspectSiteStorageUsage(ctx context.Context, containerID string) (StorageUsage, error) { if containerID == "" { return StorageUsage{}, nil } output, err := c.ExecInContainer(ctx, containerID, []string{"du", "-sb", "/app/data"}) if err != nil { // Container not running or path doesn't exist — return zero. return StorageUsage{}, nil } // du output: "12345\t/app/data\n" parts := strings.Fields(strings.TrimSpace(output)) if len(parts) == 0 { return StorageUsage{}, nil } bytes, err := strconv.ParseInt(parts[0], 10, 64) if err != nil { return StorageUsage{}, nil } return StorageUsage{UsedBytes: bytes}, nil }