mirror of
https://github.com/feeddeck/feeddeck.git
synced 2026-07-17 00:52:10 -05:00
Instead of using an `import_map.json` file to define the versions for dependencies, they are now defined directly within the import. Since the `import_map.json` file should not be used anymore and instead a `deno.json` file per function should be used, we decided to define them directly with the code. The overhead compared to a `deno.json` file per function shouldn't be that large and it makes using functions in a self-hosted setup easier.
100 lines
2.7 KiB
TypeScript
100 lines
2.7 KiB
TypeScript
import { SupabaseClient } from "jsr:@supabase/supabase-js@2";
|
|
|
|
import { ISource } from "../../models/source.ts";
|
|
import { fetchWithTimeout } from "../../utils/fetchWithTimeout.ts";
|
|
import { log } from "../../utils/log.ts";
|
|
|
|
/**
|
|
* `uploadSourceIcon` uploads the `icon` of the provided `source` to the
|
|
* Supabase storage, to avoid CORS issues within our web app and to make use of
|
|
* the built-in CDN. If the upload was successfull the path of the uploaded icon
|
|
* is returned. If the upload failed we return the original `icon` path, so that
|
|
* we can still display the icon, from it's original source.
|
|
*/
|
|
export const uploadSourceIcon = async (
|
|
supabaseClient: SupabaseClient,
|
|
source: ISource,
|
|
): Promise<string | undefined> => {
|
|
if (
|
|
!source.icon ||
|
|
source.icon === "" ||
|
|
(!source.icon.startsWith("http://") && !source.icon.startsWith("https://"))
|
|
) {
|
|
return undefined;
|
|
}
|
|
|
|
try {
|
|
const cdnIcon = await uploadFile(
|
|
supabaseClient,
|
|
"sources",
|
|
source.icon,
|
|
`${source.userId}/${source.id}.${source.icon.split(".").pop()}`,
|
|
);
|
|
|
|
if (cdnIcon) {
|
|
return cdnIcon;
|
|
}
|
|
|
|
return source.icon;
|
|
} catch (_) {
|
|
return source.icon;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* `uploadFile` uploads the provided file via it's `sourcePath` to the
|
|
* `targetPath` within the provided `bucket`. For this we have to fetch the file
|
|
* first and then upload it to the Supabase storage.
|
|
*/
|
|
const uploadFile = async (
|
|
supabaseClient: SupabaseClient,
|
|
bucket: string,
|
|
sourcePath: string,
|
|
targetPath: string,
|
|
): Promise<string | undefined> => {
|
|
try {
|
|
const fileResponse = await fetchWithTimeout(
|
|
sourcePath,
|
|
{ method: "get" },
|
|
5000,
|
|
);
|
|
const blob = await fileResponse.blob();
|
|
|
|
const { data: uploadData, error: uploadError } =
|
|
await supabaseClient.storage
|
|
.from(bucket)
|
|
.upload(
|
|
targetPath,
|
|
blobToFile(blob, targetPath.replace(/^.*[\\/]/, "")),
|
|
{
|
|
upsert: true,
|
|
},
|
|
);
|
|
if (uploadError) {
|
|
log("error", "Failed to upload source icon", {
|
|
error: uploadError,
|
|
});
|
|
return undefined;
|
|
}
|
|
|
|
return uploadData?.path;
|
|
} catch (err) {
|
|
log("error", "Failed to upload source icon", { error: err });
|
|
return undefined;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* `blobToFile` converts a Blob to a File. This is needed because the Supabase
|
|
* client only accepts File objects for uploading files. The `File` object
|
|
* will be created with the provided `fileName` and the current date as
|
|
* last modified date.
|
|
*/
|
|
const blobToFile = (blob: Blob, fileName: string): File => {
|
|
// deno-lint-ignore no-explicit-any
|
|
const b: any = blob;
|
|
b.lastModifiedDate = new Date();
|
|
b.name = fileName;
|
|
return blob as File;
|
|
};
|