@allegria/aws-file-manager 1.0.1 → 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/aws-file-manager.d.ts +318 -0
- package/dist/cjs/aws-file-manager.js +426 -0
- package/dist/cjs/aws-file-manager.js.map +1 -0
- package/dist/cjs/package.json +3 -0
- package/dist/lib/aws-file-manager.d.ts +1 -1
- package/dist/lib/aws-file-manager.js +5 -1
- package/dist/lib/aws-file-manager.js.map +1 -1
- package/package.json +27 -9
- package/dist/examples/01-setup.d.ts +0 -14
- package/dist/examples/01-setup.js +0 -42
- package/dist/examples/01-setup.js.map +0 -1
- package/dist/examples/02-upload-multer.d.ts +0 -13
- package/dist/examples/02-upload-multer.js +0 -63
- package/dist/examples/02-upload-multer.js.map +0 -1
- package/dist/examples/03-upload-web.d.ts +0 -12
- package/dist/examples/03-upload-web.js +0 -38
- package/dist/examples/03-upload-web.js.map +0 -1
- package/dist/examples/04-signed-urls.d.ts +0 -12
- package/dist/examples/04-signed-urls.js +0 -48
- package/dist/examples/04-signed-urls.js.map +0 -1
- package/dist/examples/05-download.d.ts +0 -14
- package/dist/examples/05-download.js +0 -53
- package/dist/examples/05-download.js.map +0 -1
- package/dist/examples/06-delete.d.ts +0 -17
- package/dist/examples/06-delete.js +0 -44
- package/dist/examples/06-delete.js.map +0 -1
- package/dist/examples/07-copy.d.ts +0 -14
- package/dist/examples/07-copy.js +0 -35
- package/dist/examples/07-copy.js.map +0 -1
- package/dist/examples/08-list-paginated.d.ts +0 -16
- package/dist/examples/08-list-paginated.js +0 -60
- package/dist/examples/08-list-paginated.js.map +0 -1
- package/dist/examples/09-exists.d.ts +0 -13
- package/dist/examples/09-exists.js +0 -32
- package/dist/examples/09-exists.js.map +0 -1
- package/dist/tests/aws-file-manager.test.d.ts +0 -1
- package/dist/tests/aws-file-manager.test.js +0 -359
- package/dist/tests/aws-file-manager.test.js.map +0 -1
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 02-upload-multer.ts
|
|
3
|
-
*
|
|
4
|
-
* Uploading files from an Express route that uses multer for multipart parsing.
|
|
5
|
-
*
|
|
6
|
-
* Key points:
|
|
7
|
-
* - Use `fromMulterFile()` to normalise multer's file shape into FileInput.
|
|
8
|
-
* - `upload()` returns an UploadResult with the S3 key — persist this key
|
|
9
|
-
* to your database, NOT a signed URL (URLs expire).
|
|
10
|
-
* - Generate a signed URL on demand at request time via `getSignedUrl()`.
|
|
11
|
-
*/
|
|
12
|
-
import express from "express";
|
|
13
|
-
import multer from "multer";
|
|
14
|
-
import { AwsFileManager, fromMulterFile, } from "@lib/aws-file-manager";
|
|
15
|
-
const app = express();
|
|
16
|
-
const upload = multer({ storage: multer.memoryStorage() });
|
|
17
|
-
const fileManager = new AwsFileManager({
|
|
18
|
-
region: process.env.AWS_REGION ?? "us-east-1",
|
|
19
|
-
bucketName: process.env.S3_BUCKET_NAME,
|
|
20
|
-
basePath: "uploads",
|
|
21
|
-
});
|
|
22
|
-
// POST /upload — accepts a single file field named 'file'
|
|
23
|
-
app.post("/upload", upload.single("file"), async (req, res) => {
|
|
24
|
-
if (!req.file) {
|
|
25
|
-
return res.status(400).json({ error: "No file provided" });
|
|
26
|
-
}
|
|
27
|
-
// Normalise multer's file into a FileInput
|
|
28
|
-
const fileInput = fromMulterFile(req.file);
|
|
29
|
-
// Upload to S3 with a UUID-based filename to prevent collisions
|
|
30
|
-
const result = await fileManager.upload(fileInput, {
|
|
31
|
-
folder: "avatars",
|
|
32
|
-
generateUniqueFileName: true,
|
|
33
|
-
metadata: {
|
|
34
|
-
uploadedBy: req.body.userId ?? "anonymous",
|
|
35
|
-
},
|
|
36
|
-
});
|
|
37
|
-
// Persist result.key to your database — never store the signed URL
|
|
38
|
-
// await db.files.create({ s3Key: result.key, ... });
|
|
39
|
-
// Return a short-lived signed URL for the client to use immediately
|
|
40
|
-
const url = await fileManager.getSignedUrl(result.key, {
|
|
41
|
-
disposition: "inline",
|
|
42
|
-
});
|
|
43
|
-
return res.json({
|
|
44
|
-
key: result.key,
|
|
45
|
-
url,
|
|
46
|
-
size: result.size,
|
|
47
|
-
contentType: result.contentType,
|
|
48
|
-
});
|
|
49
|
-
});
|
|
50
|
-
// POST /upload-many — accepts up to 5 files in a 'files' field
|
|
51
|
-
app.post("/upload-many", upload.array("files", 5), async (req, res) => {
|
|
52
|
-
const files = req.files;
|
|
53
|
-
if (!files?.length) {
|
|
54
|
-
return res.status(400).json({ error: "No files provided" });
|
|
55
|
-
}
|
|
56
|
-
const results = await Promise.all(files.map((file) => fileManager.upload(fromMulterFile(file), {
|
|
57
|
-
folder: "documents",
|
|
58
|
-
generateUniqueFileName: true,
|
|
59
|
-
})));
|
|
60
|
-
return res.json(results.map((r) => ({ key: r.key, size: r.size })));
|
|
61
|
-
});
|
|
62
|
-
export { app };
|
|
63
|
-
//# sourceMappingURL=02-upload-multer.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"02-upload-multer.js","sourceRoot":"","sources":["../../examples/02-upload-multer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,OAAO,MAAM,SAAS,CAAC;AAC9B,OAAO,MAAM,MAAM,QAAQ,CAAC;AAC5B,OAAO,EACL,cAAc,EACd,cAAc,GACf,MAAM,uBAAuB,CAAC;AAE/B,MAAM,GAAG,GAAG,OAAO,EAAE,CAAC;AACtB,MAAM,MAAM,GAAG,MAAM,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC,aAAa,EAAE,EAAE,CAAC,CAAC;AAE3D,MAAM,WAAW,GAAG,IAAI,cAAc,CAAC;IACrC,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,UAAU,IAAI,WAAW;IAC7C,UAAU,EAAE,OAAO,CAAC,GAAG,CAAC,cAAe;IACvC,QAAQ,EAAE,SAAS;CACpB,CAAC,CAAC;AAEH,0DAA0D;AAC1D,GAAG,CAAC,IAAI,CAAC,SAAS,EAAE,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE;IAC5D,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;QACd,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,kBAAkB,EAAE,CAAC,CAAC;IAC7D,CAAC;IAED,2CAA2C;IAC3C,MAAM,SAAS,GAAG,cAAc,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAE3C,gEAAgE;IAChE,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC,MAAM,CAAC,SAAS,EAAE;QACjD,MAAM,EAAE,SAAS;QACjB,sBAAsB,EAAE,IAAI;QAC5B,QAAQ,EAAE;YACR,UAAU,EAAE,GAAG,CAAC,IAAI,CAAC,MAAM,IAAI,WAAW;SAC3C;KACF,CAAC,CAAC;IAEH,mEAAmE;IACnE,qDAAqD;IAErD,oEAAoE;IACpE,MAAM,GAAG,GAAG,MAAM,WAAW,CAAC,YAAY,CAAC,MAAM,CAAC,GAAG,EAAE;QACrD,WAAW,EAAE,QAAQ;KACtB,CAAC,CAAC;IAEH,OAAO,GAAG,CAAC,IAAI,CAAC;QACd,GAAG,EAAE,MAAM,CAAC,GAAG;QACf,GAAG;QACH,IAAI,EAAE,MAAM,CAAC,IAAI;QACjB,WAAW,EAAE,MAAM,CAAC,WAAW;KAChC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,+DAA+D;AAC/D,GAAG,CAAC,IAAI,CAAC,cAAc,EAAE,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE;IACpE,MAAM,KAAK,GAAG,GAAG,CAAC,KAA8B,CAAC;IACjD,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,CAAC;QACnB,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,mBAAmB,EAAE,CAAC,CAAC;IAC9D,CAAC;IAED,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,GAAG,CAC/B,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CACjB,WAAW,CAAC,MAAM,CAAC,cAAc,CAAC,IAAI,CAAC,EAAE;QACvC,MAAM,EAAE,WAAW;QACnB,sBAAsB,EAAE,IAAI;KAC7B,CAAC,CACH,CACF,CAAC;IAEF,OAAO,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;AACtE,CAAC,CAAC,CAAC;AAEH,OAAO,EAAE,GAAG,EAAE,CAAC"}
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 03-upload-web.ts
|
|
3
|
-
*
|
|
4
|
-
* Uploading files from a Next.js App Router route handler (or any environment
|
|
5
|
-
* where you receive a Web API `File` object from FormData).
|
|
6
|
-
*
|
|
7
|
-
* Key points:
|
|
8
|
-
* - `fromWebFile()` is async because it calls `file.arrayBuffer()`.
|
|
9
|
-
* - Everything else is the same as the multer path.
|
|
10
|
-
*/
|
|
11
|
-
import { NextRequest } from "next/server";
|
|
12
|
-
export declare function POST(request: NextRequest): Promise<any>;
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 03-upload-web.ts
|
|
3
|
-
*
|
|
4
|
-
* Uploading files from a Next.js App Router route handler (or any environment
|
|
5
|
-
* where you receive a Web API `File` object from FormData).
|
|
6
|
-
*
|
|
7
|
-
* Key points:
|
|
8
|
-
* - `fromWebFile()` is async because it calls `file.arrayBuffer()`.
|
|
9
|
-
* - Everything else is the same as the multer path.
|
|
10
|
-
*/
|
|
11
|
-
import { NextResponse } from "next/server";
|
|
12
|
-
import { AwsFileManager, fromWebFile } from "@lib/aws-file-manager";
|
|
13
|
-
const fileManager = new AwsFileManager({
|
|
14
|
-
region: process.env.AWS_REGION ?? "us-east-1",
|
|
15
|
-
bucketName: process.env.S3_BUCKET_NAME,
|
|
16
|
-
basePath: "uploads",
|
|
17
|
-
});
|
|
18
|
-
// app/api/upload/route.ts
|
|
19
|
-
export async function POST(request) {
|
|
20
|
-
const formData = await request.formData();
|
|
21
|
-
const file = formData.get("file");
|
|
22
|
-
if (!(file instanceof File)) {
|
|
23
|
-
return NextResponse.json({ error: "No file provided" }, { status: 400 });
|
|
24
|
-
}
|
|
25
|
-
// Normalise the Web API File into a FileInput
|
|
26
|
-
const fileInput = await fromWebFile(file);
|
|
27
|
-
const result = await fileManager.upload(fileInput, {
|
|
28
|
-
folder: "profile-photos",
|
|
29
|
-
generateUniqueFileName: true,
|
|
30
|
-
});
|
|
31
|
-
// Persist result.key to your database
|
|
32
|
-
// await db.users.update({ id: userId, avatarKey: result.key });
|
|
33
|
-
const url = await fileManager.getSignedUrl(result.key, {
|
|
34
|
-
disposition: "inline",
|
|
35
|
-
});
|
|
36
|
-
return NextResponse.json({ key: result.key, url });
|
|
37
|
-
}
|
|
38
|
-
//# sourceMappingURL=03-upload-web.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"03-upload-web.js","sourceRoot":"","sources":["../../examples/03-upload-web.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAe,YAAY,EAAE,MAAM,aAAa,CAAC;AACxD,OAAO,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAEpE,MAAM,WAAW,GAAG,IAAI,cAAc,CAAC;IACrC,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,UAAU,IAAI,WAAW;IAC7C,UAAU,EAAE,OAAO,CAAC,GAAG,CAAC,cAAe;IACvC,QAAQ,EAAE,SAAS;CACpB,CAAC,CAAC;AAEH,0BAA0B;AAC1B,MAAM,CAAC,KAAK,UAAU,IAAI,CAAC,OAAoB;IAC7C,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,QAAQ,EAAE,CAAC;IAC1C,MAAM,IAAI,GAAG,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IAElC,IAAI,CAAC,CAAC,IAAI,YAAY,IAAI,CAAC,EAAE,CAAC;QAC5B,OAAO,YAAY,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,kBAAkB,EAAE,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;IAC3E,CAAC;IAED,8CAA8C;IAC9C,MAAM,SAAS,GAAG,MAAM,WAAW,CAAC,IAAI,CAAC,CAAC;IAE1C,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC,MAAM,CAAC,SAAS,EAAE;QACjD,MAAM,EAAE,gBAAgB;QACxB,sBAAsB,EAAE,IAAI;KAC7B,CAAC,CAAC;IAEH,sCAAsC;IACtC,gEAAgE;IAEhE,MAAM,GAAG,GAAG,MAAM,WAAW,CAAC,YAAY,CAAC,MAAM,CAAC,GAAG,EAAE;QACrD,WAAW,EAAE,QAAQ;KACtB,CAAC,CAAC;IAEH,OAAO,YAAY,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,MAAM,CAAC,GAAG,EAAE,GAAG,EAAE,CAAC,CAAC;AACrD,CAAC"}
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 04-signed-urls.ts
|
|
3
|
-
*
|
|
4
|
-
* Generating signed URLs for private S3 objects.
|
|
5
|
-
*
|
|
6
|
-
* Key points:
|
|
7
|
-
* - Call `getSignedUrl()` at request time — never store the returned URL.
|
|
8
|
-
* - Use `disposition: 'inline'` for images and PDFs rendered in the browser.
|
|
9
|
-
* - Use `disposition: 'attachment'` to force a file download with a filename.
|
|
10
|
-
* - Signed URLs are credentials — treat them accordingly (HTTPS only, short TTL).
|
|
11
|
-
*/
|
|
12
|
-
export {};
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 04-signed-urls.ts
|
|
3
|
-
*
|
|
4
|
-
* Generating signed URLs for private S3 objects.
|
|
5
|
-
*
|
|
6
|
-
* Key points:
|
|
7
|
-
* - Call `getSignedUrl()` at request time — never store the returned URL.
|
|
8
|
-
* - Use `disposition: 'inline'` for images and PDFs rendered in the browser.
|
|
9
|
-
* - Use `disposition: 'attachment'` to force a file download with a filename.
|
|
10
|
-
* - Signed URLs are credentials — treat them accordingly (HTTPS only, short TTL).
|
|
11
|
-
*/
|
|
12
|
-
import { AwsFileManager } from "@lib/aws-file-manager";
|
|
13
|
-
const fileManager = new AwsFileManager({
|
|
14
|
-
region: process.env.AWS_REGION ?? "us-east-1",
|
|
15
|
-
bucketName: process.env.S3_BUCKET_NAME,
|
|
16
|
-
urlExpirationSeconds: 3600, // 1 hour default
|
|
17
|
-
});
|
|
18
|
-
async function examples() {
|
|
19
|
-
const s3Key = "uploads/avatars/f47ac10b-58cc-4372-a567-0e02b2c3d479.jpg";
|
|
20
|
-
// ─── Inline (browser renders the file) ───────────────────────────────────
|
|
21
|
-
const inlineUrl = await fileManager.getSignedUrl(s3Key, {
|
|
22
|
-
disposition: "inline",
|
|
23
|
-
});
|
|
24
|
-
console.log("Inline URL:", inlineUrl);
|
|
25
|
-
// → https://my-bucket.s3.amazonaws.com/uploads/avatars/...?X-Amz-Signature=...
|
|
26
|
-
// ─── Attachment (forced download with a suggested filename) ───────────────
|
|
27
|
-
const downloadUrl = await fileManager.getSignedUrl(s3Key, {
|
|
28
|
-
disposition: "attachment",
|
|
29
|
-
fileName: "my-photo.jpg", // Suggested filename in Content-Disposition
|
|
30
|
-
});
|
|
31
|
-
console.log("Download URL:", downloadUrl);
|
|
32
|
-
// ─── Custom TTL ───────────────────────────────────────────────────────────
|
|
33
|
-
// Override the instance default for a short-lived share link
|
|
34
|
-
const shortLivedUrl = await fileManager.getSignedUrl(s3Key, {
|
|
35
|
-
disposition: "inline",
|
|
36
|
-
expiresIn: 300, // 5 minutes
|
|
37
|
-
});
|
|
38
|
-
console.log("Short-lived URL:", shortLivedUrl);
|
|
39
|
-
// ─── Serve from an API route ──────────────────────────────────────────────
|
|
40
|
-
// Typical pattern: look up the key from your DB, then redirect to the URL.
|
|
41
|
-
//
|
|
42
|
-
// GET /api/files/:id
|
|
43
|
-
// const file = await db.files.findById(req.params.id);
|
|
44
|
-
// const url = await fileManager.getSignedUrl(file.s3Key, { disposition: 'inline' });
|
|
45
|
-
// return res.redirect(302, url);
|
|
46
|
-
}
|
|
47
|
-
examples().catch(console.error);
|
|
48
|
-
//# sourceMappingURL=04-signed-urls.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"04-signed-urls.js","sourceRoot":"","sources":["../../examples/04-signed-urls.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAEvD,MAAM,WAAW,GAAG,IAAI,cAAc,CAAC;IACrC,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,UAAU,IAAI,WAAW;IAC7C,UAAU,EAAE,OAAO,CAAC,GAAG,CAAC,cAAe;IACvC,oBAAoB,EAAE,IAAI,EAAE,iBAAiB;CAC9C,CAAC,CAAC;AAEH,KAAK,UAAU,QAAQ;IACrB,MAAM,KAAK,GAAG,0DAA0D,CAAC;IAEzE,4EAA4E;IAC5E,MAAM,SAAS,GAAG,MAAM,WAAW,CAAC,YAAY,CAAC,KAAK,EAAE;QACtD,WAAW,EAAE,QAAQ;KACtB,CAAC,CAAC;IACH,OAAO,CAAC,GAAG,CAAC,aAAa,EAAE,SAAS,CAAC,CAAC;IACtC,+EAA+E;IAE/E,6EAA6E;IAC7E,MAAM,WAAW,GAAG,MAAM,WAAW,CAAC,YAAY,CAAC,KAAK,EAAE;QACxD,WAAW,EAAE,YAAY;QACzB,QAAQ,EAAE,cAAc,EAAE,4CAA4C;KACvE,CAAC,CAAC;IACH,OAAO,CAAC,GAAG,CAAC,eAAe,EAAE,WAAW,CAAC,CAAC;IAE1C,6EAA6E;IAC7E,6DAA6D;IAC7D,MAAM,aAAa,GAAG,MAAM,WAAW,CAAC,YAAY,CAAC,KAAK,EAAE;QAC1D,WAAW,EAAE,QAAQ;QACrB,SAAS,EAAE,GAAG,EAAE,YAAY;KAC7B,CAAC,CAAC;IACH,OAAO,CAAC,GAAG,CAAC,kBAAkB,EAAE,aAAa,CAAC,CAAC;IAE/C,6EAA6E;IAC7E,2EAA2E;IAC3E,EAAE;IACF,qBAAqB;IACrB,uDAAuD;IACvD,qFAAqF;IACrF,iCAAiC;AACnC,CAAC;AAED,QAAQ,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC"}
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 05-download.ts
|
|
3
|
-
*
|
|
4
|
-
* Downloading S3 objects as a Buffer or a Node.js Readable stream.
|
|
5
|
-
*
|
|
6
|
-
* Key points:
|
|
7
|
-
* - `download()` returns `null` when the object does not exist (no throw).
|
|
8
|
-
* - Use `'stream'` (default) when piping to an HTTP response or writing to disk.
|
|
9
|
-
* - Use `'buffer'` when you need the full bytes in memory (e.g. image processing).
|
|
10
|
-
*/
|
|
11
|
-
declare const app: import("express-serve-static-core").Express;
|
|
12
|
-
declare function processImage(s3Key: string): Promise<void>;
|
|
13
|
-
declare function saveToDisk(s3Key: string, outputPath: string): Promise<void>;
|
|
14
|
-
export { app, processImage, saveToDisk };
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 05-download.ts
|
|
3
|
-
*
|
|
4
|
-
* Downloading S3 objects as a Buffer or a Node.js Readable stream.
|
|
5
|
-
*
|
|
6
|
-
* Key points:
|
|
7
|
-
* - `download()` returns `null` when the object does not exist (no throw).
|
|
8
|
-
* - Use `'stream'` (default) when piping to an HTTP response or writing to disk.
|
|
9
|
-
* - Use `'buffer'` when you need the full bytes in memory (e.g. image processing).
|
|
10
|
-
*/
|
|
11
|
-
import { createWriteStream } from "fs";
|
|
12
|
-
import { pipeline } from "stream/promises";
|
|
13
|
-
import express from "express";
|
|
14
|
-
import { AwsFileManager } from "@lib/aws-file-manager";
|
|
15
|
-
const fileManager = new AwsFileManager({
|
|
16
|
-
region: process.env.AWS_REGION ?? "us-east-1",
|
|
17
|
-
bucketName: process.env.S3_BUCKET_NAME,
|
|
18
|
-
});
|
|
19
|
-
const app = express();
|
|
20
|
-
// ─── Stream directly to HTTP response ────────────────────────────────────────
|
|
21
|
-
app.get("/files/:key(*)", async (req, res) => {
|
|
22
|
-
const result = await fileManager.download(req.params.key, "stream");
|
|
23
|
-
if (!result) {
|
|
24
|
-
return res.status(404).json({ error: "File not found" });
|
|
25
|
-
}
|
|
26
|
-
const { stream, metadata } = result;
|
|
27
|
-
if (metadata.contentType)
|
|
28
|
-
res.setHeader("Content-Type", metadata.contentType);
|
|
29
|
-
if (metadata.contentLength)
|
|
30
|
-
res.setHeader("Content-Length", metadata.contentLength);
|
|
31
|
-
stream.pipe(res);
|
|
32
|
-
});
|
|
33
|
-
// ─── Download to a Buffer (for in-memory processing) ─────────────────────────
|
|
34
|
-
async function processImage(s3Key) {
|
|
35
|
-
const result = await fileManager.download(s3Key, "buffer");
|
|
36
|
-
if (!result) {
|
|
37
|
-
throw new Error(`File not found: ${s3Key}`);
|
|
38
|
-
}
|
|
39
|
-
const { buffer, metadata } = result;
|
|
40
|
-
console.log(`Downloaded ${buffer.length} bytes, type: ${metadata.contentType}`);
|
|
41
|
-
// Pass the buffer to an image processing library, compression pipeline, etc.
|
|
42
|
-
// const compressed = await sharp(buffer).jpeg({ quality: 80 }).toBuffer();
|
|
43
|
-
}
|
|
44
|
-
// ─── Stream to disk ───────────────────────────────────────────────────────────
|
|
45
|
-
async function saveToDisk(s3Key, outputPath) {
|
|
46
|
-
const result = await fileManager.download(s3Key, "stream");
|
|
47
|
-
if (!result)
|
|
48
|
-
throw new Error(`File not found: ${s3Key}`);
|
|
49
|
-
await pipeline(result.stream, createWriteStream(outputPath));
|
|
50
|
-
console.log(`Saved to ${outputPath}`);
|
|
51
|
-
}
|
|
52
|
-
export { app, processImage, saveToDisk };
|
|
53
|
-
//# sourceMappingURL=05-download.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"05-download.js","sourceRoot":"","sources":["../../examples/05-download.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,iBAAiB,EAAE,MAAM,IAAI,CAAC;AACvC,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAC3C,OAAO,OAAO,MAAM,SAAS,CAAC;AAC9B,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAEvD,MAAM,WAAW,GAAG,IAAI,cAAc,CAAC;IACrC,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,UAAU,IAAI,WAAW;IAC7C,UAAU,EAAE,OAAO,CAAC,GAAG,CAAC,cAAe;CACxC,CAAC,CAAC;AAEH,MAAM,GAAG,GAAG,OAAO,EAAE,CAAC;AAEtB,gFAAgF;AAChF,GAAG,CAAC,GAAG,CAAC,gBAAgB,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE;IAC3C,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;IAEpE,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,gBAAgB,EAAE,CAAC,CAAC;IAC3D,CAAC;IAED,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,CAAC;IAEpC,IAAI,QAAQ,CAAC,WAAW;QAAE,GAAG,CAAC,SAAS,CAAC,cAAc,EAAE,QAAQ,CAAC,WAAW,CAAC,CAAC;IAC9E,IAAI,QAAQ,CAAC,aAAa;QAAE,GAAG,CAAC,SAAS,CAAC,gBAAgB,EAAE,QAAQ,CAAC,aAAa,CAAC,CAAC;IAEpF,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACnB,CAAC,CAAC,CAAC;AAEH,gFAAgF;AAChF,KAAK,UAAU,YAAY,CAAC,KAAa;IACvC,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;IAE3D,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CAAC,mBAAmB,KAAK,EAAE,CAAC,CAAC;IAC9C,CAAC;IAED,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,CAAC;IACpC,OAAO,CAAC,GAAG,CAAC,cAAc,MAAM,CAAC,MAAM,iBAAiB,QAAQ,CAAC,WAAW,EAAE,CAAC,CAAC;IAEhF,6EAA6E;IAC7E,2EAA2E;AAC7E,CAAC;AAED,iFAAiF;AACjF,KAAK,UAAU,UAAU,CAAC,KAAa,EAAE,UAAkB;IACzD,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;IAC3D,IAAI,CAAC,MAAM;QAAE,MAAM,IAAI,KAAK,CAAC,mBAAmB,KAAK,EAAE,CAAC,CAAC;IAEzD,MAAM,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,UAAU,CAAC,CAAC,CAAC;IAC7D,OAAO,CAAC,GAAG,CAAC,YAAY,UAAU,EAAE,CAAC,CAAC;AACxC,CAAC;AAED,OAAO,EAAE,GAAG,EAAE,YAAY,EAAE,UAAU,EAAE,CAAC"}
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 06-delete.ts
|
|
3
|
-
*
|
|
4
|
-
* Deleting one or many S3 objects.
|
|
5
|
-
*
|
|
6
|
-
* Key points:
|
|
7
|
-
* - `delete()` is idempotent — calling it on a missing key does not throw.
|
|
8
|
-
* - Always soft-delete your DB record first, then call `delete()` as cleanup.
|
|
9
|
-
* A crash between the two steps leaves a recoverable S3 orphan rather than
|
|
10
|
-
* a missing file with an intact DB row.
|
|
11
|
-
* - `deleteMany()` is for removing a file and all its variants in one call.
|
|
12
|
-
* It chunks automatically at 1 000 keys to respect the S3 limit.
|
|
13
|
-
*/
|
|
14
|
-
declare function deleteFile(s3Key: string): Promise<void>;
|
|
15
|
-
declare function deleteWithVariants(originalKey: string, thumbnailKey: string, compressedKey: string): Promise<void>;
|
|
16
|
-
declare function deleteOrphans(orphanKeys: string[]): Promise<void>;
|
|
17
|
-
export { deleteFile, deleteWithVariants, deleteOrphans };
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 06-delete.ts
|
|
3
|
-
*
|
|
4
|
-
* Deleting one or many S3 objects.
|
|
5
|
-
*
|
|
6
|
-
* Key points:
|
|
7
|
-
* - `delete()` is idempotent — calling it on a missing key does not throw.
|
|
8
|
-
* - Always soft-delete your DB record first, then call `delete()` as cleanup.
|
|
9
|
-
* A crash between the two steps leaves a recoverable S3 orphan rather than
|
|
10
|
-
* a missing file with an intact DB row.
|
|
11
|
-
* - `deleteMany()` is for removing a file and all its variants in one call.
|
|
12
|
-
* It chunks automatically at 1 000 keys to respect the S3 limit.
|
|
13
|
-
*/
|
|
14
|
-
import { AwsFileManager } from "@lib/aws-file-manager";
|
|
15
|
-
const fileManager = new AwsFileManager({
|
|
16
|
-
region: process.env.AWS_REGION ?? "us-east-1",
|
|
17
|
-
bucketName: process.env.S3_BUCKET_NAME,
|
|
18
|
-
});
|
|
19
|
-
// ─── Delete a single file ─────────────────────────────────────────────────────
|
|
20
|
-
async function deleteFile(s3Key) {
|
|
21
|
-
// 1. Soft-delete the DB record first
|
|
22
|
-
// await db.files.update({ id: fileId, deletedAt: new Date() });
|
|
23
|
-
// 2. Remove the S3 object as a cleanup step
|
|
24
|
-
await fileManager.delete(s3Key);
|
|
25
|
-
console.log(`Deleted: ${s3Key}`);
|
|
26
|
-
}
|
|
27
|
-
// ─── Delete a file and all its variants ──────────────────────────────────────
|
|
28
|
-
// After uploading, your pipeline may generate additional variants (thumbnails,
|
|
29
|
-
// compressed copies). Store all their keys and delete them together.
|
|
30
|
-
async function deleteWithVariants(originalKey, thumbnailKey, compressedKey) {
|
|
31
|
-
await fileManager.deleteMany([originalKey, thumbnailKey, compressedKey]);
|
|
32
|
-
console.log("Deleted original + all variants");
|
|
33
|
-
}
|
|
34
|
-
// ─── Batch delete from a reconciliation job ───────────────────────────────────
|
|
35
|
-
// If you have a list of orphaned keys (e.g. found by `list()` but absent from
|
|
36
|
-
// your DB), delete them all at once. `deleteMany` handles chunking internally.
|
|
37
|
-
async function deleteOrphans(orphanKeys) {
|
|
38
|
-
if (orphanKeys.length === 0)
|
|
39
|
-
return;
|
|
40
|
-
await fileManager.deleteMany(orphanKeys);
|
|
41
|
-
console.log(`Deleted ${orphanKeys.length} orphaned objects`);
|
|
42
|
-
}
|
|
43
|
-
export { deleteFile, deleteWithVariants, deleteOrphans };
|
|
44
|
-
//# sourceMappingURL=06-delete.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"06-delete.js","sourceRoot":"","sources":["../../examples/06-delete.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAEvD,MAAM,WAAW,GAAG,IAAI,cAAc,CAAC;IACrC,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,UAAU,IAAI,WAAW;IAC7C,UAAU,EAAE,OAAO,CAAC,GAAG,CAAC,cAAe;CACxC,CAAC,CAAC;AAEH,iFAAiF;AACjF,KAAK,UAAU,UAAU,CAAC,KAAa;IACrC,qCAAqC;IACrC,gEAAgE;IAEhE,4CAA4C;IAC5C,MAAM,WAAW,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAChC,OAAO,CAAC,GAAG,CAAC,YAAY,KAAK,EAAE,CAAC,CAAC;AACnC,CAAC;AAED,gFAAgF;AAChF,+EAA+E;AAC/E,qEAAqE;AACrE,KAAK,UAAU,kBAAkB,CAC/B,WAAmB,EACnB,YAAoB,EACpB,aAAqB;IAErB,MAAM,WAAW,CAAC,UAAU,CAAC,CAAC,WAAW,EAAE,YAAY,EAAE,aAAa,CAAC,CAAC,CAAC;IACzE,OAAO,CAAC,GAAG,CAAC,iCAAiC,CAAC,CAAC;AACjD,CAAC;AAED,iFAAiF;AACjF,8EAA8E;AAC9E,+EAA+E;AAC/E,KAAK,UAAU,aAAa,CAAC,UAAoB;IAC/C,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO;IACpC,MAAM,WAAW,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC;IACzC,OAAO,CAAC,GAAG,CAAC,WAAW,UAAU,CAAC,MAAM,mBAAmB,CAAC,CAAC;AAC/D,CAAC;AAED,OAAO,EAAE,UAAU,EAAE,kBAAkB,EAAE,aAAa,EAAE,CAAC"}
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 07-copy.ts
|
|
3
|
-
*
|
|
4
|
-
* Copying an S3 object within the same bucket.
|
|
5
|
-
*
|
|
6
|
-
* Key points:
|
|
7
|
-
* - `copy()` is a server-side operation — no bytes move through your server.
|
|
8
|
-
* - Use it when duplicating entities that own files (e.g. cloning a template).
|
|
9
|
-
* - Pass `metadata` to override the copied object's metadata; omit it to
|
|
10
|
-
* inherit the source object's metadata (MetadataDirective=COPY).
|
|
11
|
-
*/
|
|
12
|
-
declare function cloneDocument(sourceKey: string, targetOwnerId: string): Promise<string>;
|
|
13
|
-
declare function moveObject(sourceKey: string, destKey: string): Promise<void>;
|
|
14
|
-
export { cloneDocument, moveObject };
|
package/dist/examples/07-copy.js
DELETED
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 07-copy.ts
|
|
3
|
-
*
|
|
4
|
-
* Copying an S3 object within the same bucket.
|
|
5
|
-
*
|
|
6
|
-
* Key points:
|
|
7
|
-
* - `copy()` is a server-side operation — no bytes move through your server.
|
|
8
|
-
* - Use it when duplicating entities that own files (e.g. cloning a template).
|
|
9
|
-
* - Pass `metadata` to override the copied object's metadata; omit it to
|
|
10
|
-
* inherit the source object's metadata (MetadataDirective=COPY).
|
|
11
|
-
*/
|
|
12
|
-
import { AwsFileManager } from "@lib/aws-file-manager";
|
|
13
|
-
import { randomUUID } from "crypto";
|
|
14
|
-
const fileManager = new AwsFileManager({
|
|
15
|
-
region: process.env.AWS_REGION ?? "us-east-1",
|
|
16
|
-
bucketName: process.env.S3_BUCKET_NAME,
|
|
17
|
-
basePath: "uploads",
|
|
18
|
-
});
|
|
19
|
-
// ─── Clone a template document ───────────────────────────────────────────────
|
|
20
|
-
async function cloneDocument(sourceKey, targetOwnerId) {
|
|
21
|
-
const ext = sourceKey.substring(sourceKey.lastIndexOf("."));
|
|
22
|
-
const destKey = `uploads/${targetOwnerId}/${randomUUID()}${ext}`;
|
|
23
|
-
await fileManager.copy(sourceKey, destKey);
|
|
24
|
-
console.log(`Copied ${sourceKey} → ${destKey}`);
|
|
25
|
-
return destKey;
|
|
26
|
-
}
|
|
27
|
-
// ─── Reorganise a key (rename/move) ──────────────────────────────────────────
|
|
28
|
-
// S3 has no rename — copy to the new key, then delete the old one.
|
|
29
|
-
async function moveObject(sourceKey, destKey) {
|
|
30
|
-
await fileManager.copy(sourceKey, destKey);
|
|
31
|
-
await fileManager.delete(sourceKey);
|
|
32
|
-
console.log(`Moved ${sourceKey} → ${destKey}`);
|
|
33
|
-
}
|
|
34
|
-
export { cloneDocument, moveObject };
|
|
35
|
-
//# sourceMappingURL=07-copy.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"07-copy.js","sourceRoot":"","sources":["../../examples/07-copy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AAEpC,MAAM,WAAW,GAAG,IAAI,cAAc,CAAC;IACrC,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,UAAU,IAAI,WAAW;IAC7C,UAAU,EAAE,OAAO,CAAC,GAAG,CAAC,cAAe;IACvC,QAAQ,EAAE,SAAS;CACpB,CAAC,CAAC;AAEH,gFAAgF;AAChF,KAAK,UAAU,aAAa,CAC1B,SAAiB,EACjB,aAAqB;IAErB,MAAM,GAAG,GAAG,SAAS,CAAC,SAAS,CAAC,SAAS,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC;IAC5D,MAAM,OAAO,GAAG,WAAW,aAAa,IAAI,UAAU,EAAE,GAAG,GAAG,EAAE,CAAC;IAEjE,MAAM,WAAW,CAAC,IAAI,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;IAE3C,OAAO,CAAC,GAAG,CAAC,UAAU,SAAS,MAAM,OAAO,EAAE,CAAC,CAAC;IAChD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,gFAAgF;AAChF,mEAAmE;AACnE,KAAK,UAAU,UAAU,CACvB,SAAiB,EACjB,OAAe;IAEf,MAAM,WAAW,CAAC,IAAI,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;IAC3C,MAAM,WAAW,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IACpC,OAAO,CAAC,GAAG,CAAC,SAAS,SAAS,MAAM,OAAO,EAAE,CAAC,CAAC;AACjD,CAAC;AAED,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,CAAC"}
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 08-list-paginated.ts
|
|
3
|
-
*
|
|
4
|
-
* Listing objects under a prefix, with pagination.
|
|
5
|
-
*
|
|
6
|
-
* Key points:
|
|
7
|
-
* - Each call returns up to 1 000 entries (S3 maximum per request).
|
|
8
|
-
* - `hasMore` and `continuationToken` drive the pagination loop.
|
|
9
|
-
* - Primarily used for nightly storage reconciliation jobs that walk the
|
|
10
|
-
* bucket and verify byte counts against the database.
|
|
11
|
-
*/
|
|
12
|
-
import { ListEntry } from "@lib/aws-file-manager";
|
|
13
|
-
declare function listAll(folder: string): Promise<ListEntry[]>;
|
|
14
|
-
declare function reconcileStorage(folder: string): Promise<void>;
|
|
15
|
-
declare function listByPrefix(folder: string, prefix: string): Promise<ListEntry[]>;
|
|
16
|
-
export { listAll, reconcileStorage, listByPrefix };
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 08-list-paginated.ts
|
|
3
|
-
*
|
|
4
|
-
* Listing objects under a prefix, with pagination.
|
|
5
|
-
*
|
|
6
|
-
* Key points:
|
|
7
|
-
* - Each call returns up to 1 000 entries (S3 maximum per request).
|
|
8
|
-
* - `hasMore` and `continuationToken` drive the pagination loop.
|
|
9
|
-
* - Primarily used for nightly storage reconciliation jobs that walk the
|
|
10
|
-
* bucket and verify byte counts against the database.
|
|
11
|
-
*/
|
|
12
|
-
import { AwsFileManager } from "@lib/aws-file-manager";
|
|
13
|
-
const fileManager = new AwsFileManager({
|
|
14
|
-
region: process.env.AWS_REGION ?? "us-east-1",
|
|
15
|
-
bucketName: process.env.S3_BUCKET_NAME,
|
|
16
|
-
basePath: "uploads",
|
|
17
|
-
});
|
|
18
|
-
// ─── Collect all entries under a folder ──────────────────────────────────────
|
|
19
|
-
async function listAll(folder) {
|
|
20
|
-
const allEntries = [];
|
|
21
|
-
let token;
|
|
22
|
-
do {
|
|
23
|
-
const result = await fileManager.list({
|
|
24
|
-
folder,
|
|
25
|
-
continuationToken: token,
|
|
26
|
-
});
|
|
27
|
-
allEntries.push(...result.entries);
|
|
28
|
-
token = result.continuationToken;
|
|
29
|
-
} while (token);
|
|
30
|
-
console.log(`Found ${allEntries.length} objects in '${folder}'`);
|
|
31
|
-
return allEntries;
|
|
32
|
-
}
|
|
33
|
-
// ─── Process large result sets in pages ──────────────────────────────────────
|
|
34
|
-
// Avoid accumulating everything in memory when the bucket is very large.
|
|
35
|
-
async function reconcileStorage(folder) {
|
|
36
|
-
let token;
|
|
37
|
-
let totalBytes = 0;
|
|
38
|
-
let pageCount = 0;
|
|
39
|
-
do {
|
|
40
|
-
const result = await fileManager.list({
|
|
41
|
-
folder,
|
|
42
|
-
maxResults: 500, // smaller pages for memory-constrained jobs
|
|
43
|
-
continuationToken: token,
|
|
44
|
-
});
|
|
45
|
-
pageCount++;
|
|
46
|
-
for (const entry of result.entries) {
|
|
47
|
-
totalBytes += entry.size;
|
|
48
|
-
// Compare entry.key / entry.size against your database here
|
|
49
|
-
}
|
|
50
|
-
token = result.continuationToken;
|
|
51
|
-
} while (token);
|
|
52
|
-
console.log(`Reconciled ${pageCount} page(s), total: ${totalBytes} bytes`);
|
|
53
|
-
}
|
|
54
|
-
// ─── Filter by prefix within a folder ────────────────────────────────────────
|
|
55
|
-
async function listByPrefix(folder, prefix) {
|
|
56
|
-
const result = await fileManager.list({ folder, prefix });
|
|
57
|
-
return result.entries;
|
|
58
|
-
}
|
|
59
|
-
export { listAll, reconcileStorage, listByPrefix };
|
|
60
|
-
//# sourceMappingURL=08-list-paginated.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"08-list-paginated.js","sourceRoot":"","sources":["../../examples/08-list-paginated.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,cAAc,EAAa,MAAM,uBAAuB,CAAC;AAElE,MAAM,WAAW,GAAG,IAAI,cAAc,CAAC;IACrC,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,UAAU,IAAI,WAAW;IAC7C,UAAU,EAAE,OAAO,CAAC,GAAG,CAAC,cAAe;IACvC,QAAQ,EAAE,SAAS;CACpB,CAAC,CAAC;AAEH,gFAAgF;AAChF,KAAK,UAAU,OAAO,CAAC,MAAc;IACnC,MAAM,UAAU,GAAgB,EAAE,CAAC;IACnC,IAAI,KAAyB,CAAC;IAE9B,GAAG,CAAC;QACF,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC,IAAI,CAAC;YACpC,MAAM;YACN,iBAAiB,EAAE,KAAK;SACzB,CAAC,CAAC;QACH,UAAU,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC;QACnC,KAAK,GAAG,MAAM,CAAC,iBAAiB,CAAC;IACnC,CAAC,QAAQ,KAAK,EAAE;IAEhB,OAAO,CAAC,GAAG,CAAC,SAAS,UAAU,CAAC,MAAM,gBAAgB,MAAM,GAAG,CAAC,CAAC;IACjE,OAAO,UAAU,CAAC;AACpB,CAAC;AAED,gFAAgF;AAChF,yEAAyE;AACzE,KAAK,UAAU,gBAAgB,CAAC,MAAc;IAC5C,IAAI,KAAyB,CAAC;IAC9B,IAAI,UAAU,GAAG,CAAC,CAAC;IACnB,IAAI,SAAS,GAAG,CAAC,CAAC;IAElB,GAAG,CAAC;QACF,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC,IAAI,CAAC;YACpC,MAAM;YACN,UAAU,EAAE,GAAG,EAAE,4CAA4C;YAC7D,iBAAiB,EAAE,KAAK;SACzB,CAAC,CAAC;QAEH,SAAS,EAAE,CAAC;QACZ,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;YACnC,UAAU,IAAI,KAAK,CAAC,IAAI,CAAC;YACzB,4DAA4D;QAC9D,CAAC;QAED,KAAK,GAAG,MAAM,CAAC,iBAAiB,CAAC;IACnC,CAAC,QAAQ,KAAK,EAAE;IAEhB,OAAO,CAAC,GAAG,CAAC,cAAc,SAAS,oBAAoB,UAAU,QAAQ,CAAC,CAAC;AAC7E,CAAC;AAED,gFAAgF;AAChF,KAAK,UAAU,YAAY,CAAC,MAAc,EAAE,MAAc;IACxD,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IAC1D,OAAO,MAAM,CAAC,OAAO,CAAC;AACxB,CAAC;AAED,OAAO,EAAE,OAAO,EAAE,gBAAgB,EAAE,YAAY,EAAE,CAAC"}
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 09-exists.ts
|
|
3
|
-
*
|
|
4
|
-
* Checking whether an S3 object exists at a given key.
|
|
5
|
-
*
|
|
6
|
-
* Key points:
|
|
7
|
-
* - `exists()` returns `false` for missing keys; it does not throw.
|
|
8
|
-
* - Useful as a lightweight data integrity check before an operation.
|
|
9
|
-
* - Not a replacement for proper error handling — network errors still throw.
|
|
10
|
-
*/
|
|
11
|
-
declare function assertFileExists(s3Key: string): Promise<void>;
|
|
12
|
-
declare function uploadIfAbsent(s3Key: string, uploadFn: () => Promise<void>): Promise<void>;
|
|
13
|
-
export { assertFileExists, uploadIfAbsent };
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 09-exists.ts
|
|
3
|
-
*
|
|
4
|
-
* Checking whether an S3 object exists at a given key.
|
|
5
|
-
*
|
|
6
|
-
* Key points:
|
|
7
|
-
* - `exists()` returns `false` for missing keys; it does not throw.
|
|
8
|
-
* - Useful as a lightweight data integrity check before an operation.
|
|
9
|
-
* - Not a replacement for proper error handling — network errors still throw.
|
|
10
|
-
*/
|
|
11
|
-
import { AwsFileManager } from "@lib/aws-file-manager";
|
|
12
|
-
const fileManager = new AwsFileManager({
|
|
13
|
-
region: process.env.AWS_REGION ?? "us-east-1",
|
|
14
|
-
bucketName: process.env.S3_BUCKET_NAME,
|
|
15
|
-
});
|
|
16
|
-
// ─── Guard before serving a file ─────────────────────────────────────────────
|
|
17
|
-
async function assertFileExists(s3Key) {
|
|
18
|
-
const found = await fileManager.exists(s3Key);
|
|
19
|
-
if (!found) {
|
|
20
|
-
throw new Error(`Storage integrity error: file missing at key "${s3Key}"`);
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
// ─── Skip re-uploading if the file is already there ──────────────────────────
|
|
24
|
-
async function uploadIfAbsent(s3Key, uploadFn) {
|
|
25
|
-
if (await fileManager.exists(s3Key)) {
|
|
26
|
-
console.log(`Skipping upload — key already exists: ${s3Key}`);
|
|
27
|
-
return;
|
|
28
|
-
}
|
|
29
|
-
await uploadFn();
|
|
30
|
-
}
|
|
31
|
-
export { assertFileExists, uploadIfAbsent };
|
|
32
|
-
//# sourceMappingURL=09-exists.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"09-exists.js","sourceRoot":"","sources":["../../examples/09-exists.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAEvD,MAAM,WAAW,GAAG,IAAI,cAAc,CAAC;IACrC,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,UAAU,IAAI,WAAW;IAC7C,UAAU,EAAE,OAAO,CAAC,GAAG,CAAC,cAAe;CACxC,CAAC,CAAC;AAEH,gFAAgF;AAChF,KAAK,UAAU,gBAAgB,CAAC,KAAa;IAC3C,MAAM,KAAK,GAAG,MAAM,WAAW,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAC9C,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CAAC,iDAAiD,KAAK,GAAG,CAAC,CAAC;IAC7E,CAAC;AACH,CAAC;AAED,gFAAgF;AAChF,KAAK,UAAU,cAAc,CAC3B,KAAa,EACb,QAA6B;IAE7B,IAAI,MAAM,WAAW,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;QACpC,OAAO,CAAC,GAAG,CAAC,yCAAyC,KAAK,EAAE,CAAC,CAAC;QAC9D,OAAO;IACT,CAAC;IACD,MAAM,QAAQ,EAAE,CAAC;AACnB,CAAC;AAED,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|