1import * as GCP from "alchemy/GCP";
2
3/** Only objects under this prefix trigger the indexer. */
4export const UPLOADS_PREFIX = "uploads/";
5
6/**
7 * Where the uploaded bytes live. `forceDestroy` empties the bucket on
8 * `alchemy destroy`; drop it in production so a destroy cannot take user
9 * data with it.
10 */
11export const Uploads = GCP.Storage.Bucket("Uploads", {
12 forceDestroy: true,
13});
14
15/**
16 * One Firestore document per file, at `files/{name}`, written by the
17 * indexer once the object has landed.
18 */
19export const Files = GCP.Firestore.Database("Files", {
20 type: "FIRESTORE_NATIVE",
21});
22
23/**
24 * File names double as Firestore document ids and object-name suffixes,
25 * so keep them to a URL- and id-safe alphabet.
26 */
27export const isValidName = (name: string) =>
28 /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/.test(name);
29
30export const objectFor = (name: string) => `${UPLOADS_PREFIX}${name}`;
31
32export const documentFor = (name: string) => `files/${name}`;
33
34/** What the indexer records for each upload. */
35export interface FileRecord {
36 name: string;
37 object: string;
38 generation: string;
39 size: number;
40 contentType: string;
41 sha256: string;
42 indexedAt: Date;
43}