feat(storage): implement filesystem backend #127
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Implement the
FileStoreinterface (from #126) for local filesystem storage, writing files under a configurable root directory.Context
The
FileStoreinterface definesPut,Get,GetVersion,Delete,Exists,Copy,PresignPut, andPing. The filesystem backend replaces MinIO for deployments that want simpler infrastructure — files stored directly on disk at a configurable path (e.g./var/lib/silo/objects).Object key patterns used by the application
items/{partNumber}/rev{N}.FCStdstorage.FileKey()thumbnails/{partNumber}/rev{N}.pngstorage.ThumbnailKey()uploads/tmp/{uuid}/{filename}HandlePresignUploaditems/{itemID}/files/{uuid}/{filename}HandleAssociateItemFileitems/{itemID}/thumbnail.pngHandleSetItemThumbnailAll of these become filesystem paths under
{root}/{key}.Requirements
Create
internal/storage/filesystem.goimplementingFileStore:Put(ctx, key, reader, size, contentType) (*PutResult, error){root}/{key}os.MkdirAllos.Renameto final pathPutResult{Key: key, Size: bytesWritten, Checksum: sha256hex}VersionIDcan be empty string (filesystem has no versioning){root}/{key}.meta(JSON:{"content_type": "..."}) or skip if not needed for servingGet(ctx, key) (io.ReadCloser, error)os.Open("{root}/{key}")returning the*os.File(implementsio.ReadCloser)os.ErrNotExist-wrapped error if file missingGetVersion(ctx, key, versionID) (io.ReadCloser, error)versionID, delegate toGet(ctx, key)versionIDis non-emptyDelete(ctx, key) erroros.Remove("{root}/{key}")Exists(ctx, key) (bool, error)os.Stat("{root}/{key}")— return(true, nil)if exists,(false, nil)ifos.IsNotExistCopy(ctx, srcKey, dstKey) errorHandleAssociateItemFileandHandleSetItemThumbnailto move temp uploads to permanent locationsPresignPut(ctx, key, expiry) (*url.URL, error)("", ErrPresignNotSupported)or a sentinel errorHandlePresignUpload) must handle this — see #129Ping(ctx) errorConstructor
File structure
Testing
Unit tests in
internal/storage/filesystem_test.go:TestPut— write file, verify content and checksumTestPutAtomicity— verify no partial files on write failureTestGet— read back written fileTestGetMissing— error on nonexistent keyTestDelete— delete file, verify goneTestDeleteMissing— no error on missing fileTestExists— true for existing, false for missingTestCopy— verify content matches after copyTestPing— succeeds with valid root, fails with invalidUse
t.TempDir()for test isolation.Acceptance criteria
FilesystemStoreimplementsFileStoreinterfacePutgo build ./...passesPriority
P0 — blocks upload endpoint changes and data migration
Depends on
Part of
Storage Migration: MinIO → PostgreSQL + Filesystem