b622384774
Build / build (push) Successful in 10m21s
- Add storage_enabled and storage_limit_mb columns to static_sites.
- Create/attach Docker volumes (tinyforge-site-{name}-data) for Deno
sites with storage enabled, mounted at /app/data.
- Grant --allow-write=/app/data in Deno container CMD.
- Add storage usage API endpoint (GET /api/sites/{id}/storage).
- Show storage section in site detail page with usage bar.
- Add storage toggle and limit field to new site wizard.
- Use ConfirmDialog for secret deletion instead of inline delete.
84 lines
2.2 KiB
Go
84 lines
2.2 KiB
Go
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
|
|
}
|