Add support for storing images in S3 (#896)

This commit is contained in:
solidDoWant
2025-04-20 11:47:49 -05:00
committed by GitHub
parent 1d1ea295c0
commit 099d893da9
3 changed files with 29 additions and 9 deletions
+11 -5
View File
@@ -1,5 +1,5 @@
import { stat } from "node:fs/promises";
import type { BunFile } from "bun";
import type { Stats } from "node:fs";
import type { BunFile, S3File, S3Stats } from "bun";
import { type SQL, and, eq, sql } from "drizzle-orm";
import Elysia, { type Context, t } from "elysia";
import { prefix } from "~/base";
@@ -15,6 +15,7 @@ import { sqlarr } from "~/db/utils";
import { KError } from "~/models/error";
import { bubble } from "~/models/examples";
import { AcceptLanguage, isUuid, processLanguages } from "~/models/utils";
import { getFile } from "~/utils";
import { imageDir } from "./seed/images";
function getRedirectToImageHandler({
@@ -98,7 +99,7 @@ export const imagesH = new Elysia({ tags: ["images"] })
"/images/:id",
async ({ params: { id }, query: { quality }, headers: reqHeaders }) => {
const path = `${imageDir}/${id}.${quality}.jpg`;
const file = Bun.file(path);
const file = getFile(path);
const etag = await generateETag(file);
if (await isCached(reqHeaders, etag, path))
@@ -366,8 +367,13 @@ export async function isCached(
if (headers["if-modified-since"]) {
const ifModifiedSince = headers["if-modified-since"];
let lastModified: Date | undefined;
const stat = await getFile(filePath).stat();
try {
lastModified = (await stat(filePath)).mtime;
if ((stat as S3Stats).lastModified) {
lastModified = (stat as S3Stats).lastModified;
} else if ((stat as Stats).mtime) {
lastModified = (stat as Stats).mtime;
}
} catch {
/* empty */
}
@@ -382,7 +388,7 @@ export async function isCached(
return false;
}
export async function generateETag(file: BunFile) {
export async function generateETag(file: BunFile | S3File) {
const hash = new Bun.CryptoHasher("md5");
hash.update(await file.arrayBuffer());
+4 -4
View File
@@ -1,4 +1,3 @@
import { mkdir, writeFile } from "node:fs/promises";
import path from "node:path";
import { encode } from "blurhash";
import { type SQL, and, eq, is, lt, sql } from "drizzle-orm";
@@ -9,10 +8,9 @@ import sharp from "sharp";
import { type Transaction, db } from "~/db";
import { mqueue } from "~/db/schema/mqueue";
import type { Image } from "~/models/utils";
import { getFile } from "~/utils";
export const imageDir = process.env.IMAGES_PATH ?? "./images";
await mkdir(imageDir, { recursive: true });
export const defaultBlurhash = "000000";
type ImageTask = {
@@ -164,7 +162,9 @@ async function downloadImage(id: string, url: string): Promise<string> {
await Promise.all(
Object.entries(resolutions).map(async ([resolution, dimensions]) => {
const buffer = await image.clone().resize(dimensions.width).toBuffer();
await writeFile(path.join(imageDir, `${id}.${resolution}.jpg`), buffer);
const file = getFile(path.join(imageDir, `${id}.${resolution}.jpg`));
await Bun.write(file, buffer, { mode: 0o660 });
}),
);