Files
git-pages/src/backend.go
Catherine c250922f8d Allow domains to be administratively frozen.
The following script may be used to handle abusive sites:

    cd $(mktemp -d)
    echo "<h1>Gone</h1>" >index.html
    echo "/* /index.html 410" >_redirects
    tar cf site.tar index.html _redirects
    git-pages -update-site $1 site.tar
    git-pages -freeze-domain $1
2025-12-02 23:56:01 +00:00

101 lines
3.2 KiB
Go

package git_pages
import (
"context"
"errors"
"fmt"
"io"
"slices"
"strings"
"time"
)
var ErrObjectNotFound = errors.New("not found")
var ErrDomainFrozen = errors.New("domain administratively frozen")
func splitBlobName(name string) []string {
algo, hash, found := strings.Cut(name, "-")
if found {
return slices.Concat([]string{algo}, splitBlobName(hash))
} else {
return []string{name[0:2], name[2:4], name[4:]}
}
}
type BackendFeature string
const (
FeatureCheckDomainMarker BackendFeature = "check-domain-marker"
)
type GetManifestOptions struct {
BypassCache bool
}
type Backend interface {
// Returns true if the feature has been enabled for this store, false otherwise.
HasFeature(ctx context.Context, feature BackendFeature) bool
// Enables the feature for this store.
EnableFeature(ctx context.Context, feature BackendFeature) error
// Retrieve a blob. Returns `reader, size, mtime, err`.
GetBlob(ctx context.Context, name string) (
reader io.ReadSeeker, size uint64, mtime time.Time, err error,
)
// Store a blob. If a blob called `name` already exists, this function returns `nil` without
// regards to the old or new contents. It is expected that blobs are content-addressed, i.e.
// the `name` contains a cryptographic hash of `data`, but the backend is ignorant of this.
PutBlob(ctx context.Context, name string, data []byte) error
// Delete a blob. This is an unconditional operation that can break integrity of manifests.
DeleteBlob(ctx context.Context, name string) error
// Retrieve a manifest.
GetManifest(ctx context.Context, name string, opts GetManifestOptions) (
manifest *Manifest, mtime time.Time, err error,
)
// Stage a manifest. This operation stores a new version of a manifest, locking any blobs
// referenced from it in place (for garbage collection purposes) but without any other side
// effects.
StageManifest(ctx context.Context, manifest *Manifest) error
// Commit a manifest. This is an atomic operation; `GetManifest` calls will return either
// the old version or the new version of the manifest, never anything else.
CommitManifest(ctx context.Context, name string, manifest *Manifest) error
// Delete a manifest.
DeleteManifest(ctx context.Context, name string) error
// List all manifests.
ListManifests(ctx context.Context) (manifests []string, err error)
// Check whether a domain has any deployments.
CheckDomain(ctx context.Context, domain string) (found bool, err error)
// Creates a domain. This allows us to start serving content for the domain.
CreateDomain(ctx context.Context, domain string) error
// Freeze or thaw a domain. This allows a site to be administratively locked, e.g. if it
// is discovered serving abusive content.
FreezeDomain(ctx context.Context, domain string, freeze bool) error
}
func CreateBackend(config *StorageConfig) (backend Backend, err error) {
switch config.Type {
case "fs":
if backend, err = NewFSBackend(&config.FS); err != nil {
err = fmt.Errorf("fs backend: %w", err)
}
case "s3":
if backend, err = NewS3Backend(context.Background(), &config.S3); err != nil {
err = fmt.Errorf("s3 backend: %w", err)
}
default:
err = fmt.Errorf("unknown backend: %s", config.Type)
}
return
}