oficina / sheets Public

Clone
git clone https://amadan.net/oficina/sheets

Plain git — no account needed to clone.

Download

Download this file

1package xlsx
2
3import (
4 "bytes"
5 "context"
6 "fmt"
7
8 "amadan.net/rastrillo/rastrillo/blobs"
9
10 "amadan.net/oficina/sheets/internal/sheets/store"
11)
12
13// blobContentType is deliberately opaque. blobs.Sealed encrypts the bytes
14// but passes ContentType through as visible metadata (verified directly
15// against rastrillo/blobs — see blobs.Sealed's own doc comment), so a
16// truthful spreadsheet content type would announce exactly what the
17// ciphertext is, in a bucket that is otherwise opaque. Spec §2.3, §11.
18const blobContentType = "application/octet-stream"
19
20// StoreSealed exports h's tab and writes it to underlying through
21// blobs.Sealed under key, never letting a plaintext spreadsheet reach
22// underlying — spec §0 and §2.3: unsealed on the S3 backend, a generated
23// XLSX file is a plaintext spreadsheet in a bucket, the one rule defeated
24// through a side door.
25//
26// This exists because the sealing step belongs next to the bytes it
27// seals, not because the rest of the plumbing does: the upload/download
28// HTTP surface and the background-job wrapper around Import/Export
29// (spec §11: "Import runs as a rastrillo background job") are plan 2 —
30// this repo has no job runner, no session-gated routes, and no app-row
31// shape for a stored blob's Ref yet. A caller with all of that wires it
32// to StoreSealed/LoadSealed rather than to underlying directly.
33//
34// The blob address returned is the ciphertext's hash (dedup is per key,
35// not per plaintext) and Ref.Size is the sealed size — both documented on
36// blobs.Sealed itself, restated here because a caller showing either to
37// a person (a quota, a progress bar) needs to know.
38func StoreSealed(ctx context.Context, underlying blobs.Store, key []byte, h *store.Handle) (blobs.Ref, error) {
39 var buf bytes.Buffer
40 if err := Export(h, &buf); err != nil {
41 return blobs.Ref{}, err
42 }
43 ref, err := blobs.Sealed(underlying, key).Put(ctx, &buf, blobContentType)
44 if err != nil {
45 return blobs.Ref{}, fmt.Errorf("xlsx: store sealed export: %w", err)
46 }
47 return ref, nil
48}
49
50// LoadSealed reads a blob written by StoreSealed (or any XLSX blob sealed
51// under key with blobs.Sealed) and imports it into h.
52func LoadSealed(ctx context.Context, underlying blobs.Store, key []byte, ref blobs.Ref, h *store.Handle) error {
53 rc, err := blobs.Sealed(underlying, key).Get(ctx, ref.Hash)
54 if err != nil {
55 return fmt.Errorf("xlsx: load sealed import: %w", err)
56 }
57 defer rc.Close()
58 return Import(rc, h)
59}
60