Go · 3416 bytes Raw Blame History
1 // SPDX-License-Identifier: AGPL-3.0-or-later
2
3 // Package storage provides shithub's storage abstractions:
4 // - ObjectStore: a small interface for S3-compatible object storage
5 // (works against MinIO in dev/test and DigitalOcean Spaces in prod).
6 // - reposfs: filesystem-backed bare git repository helpers (sharded
7 // layout, atomic helpers, strict path validation).
8 // - Quota: a placeholder type for disk-quota plumbing (no enforcement
9 // yet — wired up in later sprints).
10 //
11 // Path validation is the security boundary: every entry point that takes
12 // owner/repo names from outside the package goes through RepoPath, which
13 // rejects unsafe inputs against a strict whitelist.
14 package storage
15
16 import (
17 "context"
18 "io"
19 "time"
20 )
21
22 // ObjectStore is the abstract interface every storage backend implements.
23 // Implementations: s3 (any S3-compatible endpoint via minio-go) and memory
24 // (in-process map for tests).
25 type ObjectStore interface {
26 // Put writes body to key. Returns the resulting object's metadata
27 // (etag, size). Honors opts.IfNoneMatch (when "*", fails with
28 // ErrPreconditionFailed if the key already exists).
29 Put(ctx context.Context, key string, body io.Reader, opts PutOpts) (PutResult, error)
30
31 // Get returns a reader for the object at key. Caller must Close.
32 // Returns ErrNotFound when key is absent.
33 Get(ctx context.Context, key string) (io.ReadCloser, ObjectMeta, error)
34
35 // Stat returns metadata for key without fetching the body.
36 // Returns ErrNotFound when key is absent.
37 Stat(ctx context.Context, key string) (ObjectMeta, error)
38
39 // Delete removes key. Returns nil for a missing key (idempotent).
40 Delete(ctx context.Context, key string) error
41
42 // List enumerates objects under prefix. Pagination via opts.ContinuationToken.
43 List(ctx context.Context, prefix string, opts ListOpts) (ListResult, error)
44
45 // SignedURL returns a pre-signed URL for the given method ("GET" or
46 // "PUT") on key, valid for ttl. The URL grants direct browser/client
47 // access without exposing credentials — used for avatar/attachment
48 // uploads and large downloads in later sprints.
49 SignedURL(ctx context.Context, key string, ttl time.Duration, method string) (string, error)
50 }
51
52 // PutOpts controls a Put.
53 type PutOpts struct {
54 ContentType string
55 // IfNoneMatch, when "*", causes Put to fail with ErrPreconditionFailed
56 // if the destination already exists. Other values are not supported.
57 IfNoneMatch string
58 // ContentLength, when > 0, is passed to the backend as a hint. When 0,
59 // the backend buffers / streams as needed.
60 ContentLength int64
61 }
62
63 // PutResult is what Put returns.
64 type PutResult struct {
65 ETag string
66 Size int64
67 }
68
69 // ObjectMeta is the metadata returned by Get/Stat.
70 type ObjectMeta struct {
71 Key string
72 Size int64
73 ETag string
74 ContentType string
75 LastModified time.Time
76 }
77
78 // ListOpts controls a List.
79 type ListOpts struct {
80 // ContinuationToken resumes pagination from a prior page.
81 ContinuationToken string
82 // MaxKeys caps the page size. Zero means backend default.
83 MaxKeys int
84 // Recursive, when false, treats "/" as a delimiter and surfaces common
85 // prefixes (folders) in ListResult.CommonPrefixes.
86 Recursive bool
87 }
88
89 // ListResult is one page of a List.
90 type ListResult struct {
91 Objects []ObjectMeta
92 CommonPrefixes []string
93 NextContinuationToken string
94 IsTruncated bool
95 }
96