forked from Mike/freno-dev
Dead code: 77 verified-unused exports, files (BackArrow, MenuBars, cookies.ts, db/create.ts, schemas/comment.ts, security-headers.ts) and 12 unused dependencies removed Comments: ~370 RESTATE comments stripped across 53 files; 2 verbose blocks tightened; dead commented-out config removed Complexity: bulkUpsert extracted into 11 per-entity helpers (CCN 156->~10); login formHandler split into 3 submitters (CCN 63->~5); account page render split into 8 section components (CCN 40); updatePost SQL builder rebuilt; assert*Owned consolidated behind generic assertOwnedBy Defensive guards: 4 redundant rethrow/nullish guards removed
104 lines
2.7 KiB
TypeScript
104 lines
2.7 KiB
TypeScript
/**
|
|
* S3 Upload Utility for SolidStart
|
|
* Uploads files to S3 using pre-signed URLs from tRPC
|
|
* Automatically converts images to WebP format for better compression
|
|
*/
|
|
|
|
import { api } from "~/lib/api";
|
|
import { resizeImage, convertToWebP } from "~/lib/resize-utils";
|
|
|
|
export default async function AddImageToS3(
|
|
file: Blob | File,
|
|
title: string,
|
|
type: string
|
|
): Promise<string | undefined> {
|
|
try {
|
|
const filename = (file as File).name;
|
|
|
|
const ext = /^.+\.([^.]+)$/.exec(filename);
|
|
let contentType = "application/octet-stream";
|
|
let isImage = false;
|
|
let isVideo = false;
|
|
|
|
if (ext) {
|
|
const extension = ext[1].toLowerCase();
|
|
if (["mp4", "webm", "mov", "quicktime"].includes(extension)) {
|
|
contentType =
|
|
extension === "mov" ? "video/quicktime" : `video/${extension}`;
|
|
isVideo = true;
|
|
} else if (["jpg", "jpeg", "png", "gif", "webp"].includes(extension)) {
|
|
isImage = true;
|
|
}
|
|
}
|
|
|
|
let fileToUpload: Blob | File = file;
|
|
let finalFilename = filename;
|
|
|
|
// Convert images to WebP for better compression
|
|
if (isImage) {
|
|
contentType = "image/webp";
|
|
fileToUpload = await convertToWebP(file, 0.85);
|
|
finalFilename = filename.replace(/\.[^.]+$/, ".webp");
|
|
}
|
|
|
|
const { uploadURL, key } = await api.misc.getPreSignedURL.mutate({
|
|
type,
|
|
title,
|
|
filename: finalFilename
|
|
});
|
|
|
|
console.log("url: " + uploadURL, "key: " + key);
|
|
|
|
const uploadResponse = await fetch(uploadURL, {
|
|
method: "PUT",
|
|
headers: {
|
|
"Content-Type": contentType
|
|
},
|
|
body: fileToUpload
|
|
});
|
|
|
|
if (!uploadResponse.ok) {
|
|
throw new Error("Failed to upload file to S3");
|
|
}
|
|
|
|
if (type === "blog" && isImage) {
|
|
try {
|
|
const thumbnail = await resizeImage(file, 200, 200, 0.8);
|
|
|
|
const thumbnailFilename = finalFilename.replace(
|
|
/(\.[^.]+)$/,
|
|
"-small$1"
|
|
);
|
|
|
|
const { uploadURL: thumbnailUploadURL } =
|
|
await api.misc.getPreSignedURL.mutate({
|
|
type,
|
|
title,
|
|
filename: thumbnailFilename
|
|
});
|
|
|
|
const thumbnailUploadResponse = await fetch(thumbnailUploadURL, {
|
|
method: "PUT",
|
|
headers: {
|
|
"Content-Type": "image/webp"
|
|
},
|
|
body: thumbnail
|
|
});
|
|
|
|
if (!thumbnailUploadResponse.ok) {
|
|
console.error("Failed to upload thumbnail to S3");
|
|
} else {
|
|
console.log("Thumbnail uploaded successfully");
|
|
}
|
|
} catch (thumbnailError) {
|
|
console.error("Thumbnail creation/upload failed:", thumbnailError);
|
|
}
|
|
}
|
|
|
|
return key;
|
|
} catch (e) {
|
|
console.error("S3 upload error:", e);
|
|
throw e;
|
|
}
|
|
}
|