mirror of
https://github.com/shishantbiswas/bknd.git
synced 2026-03-15 20:17:22 +00:00
fix media mime guessing when uploading to entity
This commit is contained in:
@@ -111,6 +111,7 @@ describe("EventManager", async () => {
|
||||
emgr.onEvent(ReturnEvent, async () => "1", "sync");
|
||||
emgr.onEvent(ReturnEvent, async () => "0", "sync");
|
||||
|
||||
// @todo: fix this
|
||||
// @ts-expect-error must be string
|
||||
emgr.onEvent(ReturnEvent, async () => 0, "sync");
|
||||
|
||||
|
||||
@@ -34,4 +34,21 @@ describe("media/mime-types", () => {
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
test("isMimeType", () => {
|
||||
expect(tiny.isMimeType("image/jpeg")).toBe(true);
|
||||
expect(tiny.isMimeType("image/jpeg", ["image/png"])).toBe(true);
|
||||
expect(tiny.isMimeType("image/png", ["image/png"])).toBe(false);
|
||||
expect(tiny.isMimeType("image/png")).toBe(true);
|
||||
expect(tiny.isMimeType("whatever")).toBe(false);
|
||||
expect(tiny.isMimeType("text/tab-separated-values")).toBe(true);
|
||||
});
|
||||
|
||||
test("extension", () => {
|
||||
expect(tiny.extension("image/png")).toBe("png");
|
||||
expect(tiny.extension("image/jpeg")).toBe("jpeg");
|
||||
expect(tiny.extension("application/zip")).toBe("zip");
|
||||
expect(tiny.extension("text/tab-separated-values")).toBe("tsv");
|
||||
expect(tiny.extension("application/zip")).toBe("zip");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -124,8 +124,9 @@ export class AppMedia extends Module<typeof mediaConfigSchema> {
|
||||
const mutator = em.mutator(media);
|
||||
mutator.__unstable_toggleSystemEntityCreation(false);
|
||||
const payload = this.uploadedEventDataToMediaPayload(e.params);
|
||||
await mutator.insertOne(payload);
|
||||
const { data } = await mutator.insertOne(payload);
|
||||
mutator.__unstable_toggleSystemEntityCreation(true);
|
||||
return { data };
|
||||
},
|
||||
{ mode: "sync", id: "add-data-media" }
|
||||
);
|
||||
|
||||
@@ -72,11 +72,8 @@ export class MediaController extends Controller {
|
||||
|
||||
// upload file
|
||||
// @todo: add required type for "upload endpoints"
|
||||
hono.post("/upload/:filename", async (c) => {
|
||||
const { filename } = c.req.param();
|
||||
if (!filename) {
|
||||
throw new Error("No file name provided");
|
||||
}
|
||||
hono.post("/upload/:filename?", async (c) => {
|
||||
const reqname = c.req.param("filename");
|
||||
|
||||
const body = await getFileFromContext(c);
|
||||
if (!body) {
|
||||
@@ -89,7 +86,9 @@ export class MediaController extends Controller {
|
||||
);
|
||||
}
|
||||
|
||||
const filename = reqname ?? getRandomizedFilename(body as File);
|
||||
const res = await this.getStorage().uploadFile(body, filename);
|
||||
|
||||
return c.json(res, HttpStatus.CREATED);
|
||||
});
|
||||
|
||||
@@ -191,8 +190,8 @@ export class MediaController extends Controller {
|
||||
);
|
||||
}
|
||||
|
||||
const file_name = getRandomizedFilename(file as File);
|
||||
const info = await this.getStorage().uploadFile(file, file_name, true);
|
||||
const filename = getRandomizedFilename(file as File);
|
||||
const info = await this.getStorage().uploadFile(file, filename, true);
|
||||
|
||||
const mutator = this.media.em.mutator(media_entity);
|
||||
mutator.__unstable_toggleSystemEntityCreation(false);
|
||||
|
||||
@@ -119,7 +119,10 @@ export class Storage implements EmitsEvents {
|
||||
}
|
||||
};
|
||||
if (!noEmit) {
|
||||
await this.emgr.emit(new StorageEvents.FileUploadedEvent(eventData));
|
||||
const result = await this.emgr.emit(new StorageEvents.FileUploadedEvent(eventData));
|
||||
if (result.returned) {
|
||||
return result.params;
|
||||
}
|
||||
}
|
||||
|
||||
return eventData;
|
||||
|
||||
@@ -1,11 +1,23 @@
|
||||
import { Event } from "core/events";
|
||||
import { Event, InvalidEventReturn } from "core/events";
|
||||
import type { FileBody, FileUploadPayload } from "../Storage";
|
||||
|
||||
export type FileUploadedEventData = FileUploadPayload & {
|
||||
file: FileBody;
|
||||
};
|
||||
export class FileUploadedEvent extends Event<FileUploadedEventData> {
|
||||
export class FileUploadedEvent extends Event<FileUploadedEventData, object> {
|
||||
static override slug = "file-uploaded";
|
||||
|
||||
override validate(data: object) {
|
||||
if (typeof data !== "object") {
|
||||
throw new InvalidEventReturn("object", typeof data);
|
||||
}
|
||||
|
||||
return this.clone({
|
||||
// prepending result, so original is always kept
|
||||
...data,
|
||||
...this.params
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class FileDeletedEvent extends Event<{ name: string }> {
|
||||
|
||||
@@ -77,6 +77,17 @@ export function guess(f: string): string {
|
||||
}
|
||||
|
||||
export function isMimeType(mime: any, exclude: string[] = []) {
|
||||
if (exclude.includes(mime)) return false;
|
||||
|
||||
// try quick first
|
||||
if (
|
||||
Object.entries(Q)
|
||||
.flatMap(([t, e]) => e.map((x) => `${t}/${x}`))
|
||||
.includes(mime)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
for (const [k, v] of M.entries()) {
|
||||
if (v === mime && !exclude.includes(k)) {
|
||||
return true;
|
||||
@@ -86,6 +97,14 @@ export function isMimeType(mime: any, exclude: string[] = []) {
|
||||
}
|
||||
|
||||
export function extension(mime: string) {
|
||||
for (const [t, e] of Object.entries(Q)) {
|
||||
for (const _e of e) {
|
||||
if (mime === `${t}/${_e}`) {
|
||||
return _e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const [k, v] of M.entries()) {
|
||||
if (v === mime) {
|
||||
return k;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { randomString } from "core/utils";
|
||||
import { isFile, randomString } from "core/utils";
|
||||
import { extension } from "media/storage/mime-types-tiny";
|
||||
|
||||
export function getExtension(filename: string): string | undefined {
|
||||
export function getExtensionFromName(filename: string): string | undefined {
|
||||
if (!filename.includes(".")) return;
|
||||
|
||||
const parts = filename.split(".");
|
||||
@@ -17,6 +18,12 @@ export function getRandomizedFilename(file: File | string, length = 16): string
|
||||
throw new Error("Invalid file name");
|
||||
}
|
||||
|
||||
// @todo: use uuid instead?
|
||||
return [randomString(length), getExtension(filename)].filter(Boolean).join(".");
|
||||
let ext = getExtensionFromName(filename);
|
||||
if (isFile(file) && file.type) {
|
||||
const _ext = extension(file.type);
|
||||
if (_ext.length > 0) ext = _ext;
|
||||
}
|
||||
|
||||
// @todo: use uuid instead?
|
||||
return [randomString(length), ext].filter(Boolean).join(".");
|
||||
}
|
||||
|
||||
@@ -279,12 +279,14 @@ export function Dropzone({
|
||||
const response = JSON.parse(xhr.responseText);
|
||||
|
||||
console.log("Response:", file, response);
|
||||
console.log("New state", response.state);
|
||||
replaceFileState(file.path, {
|
||||
const newState = {
|
||||
...response.state,
|
||||
progress: 1,
|
||||
state: "uploaded"
|
||||
});
|
||||
};
|
||||
|
||||
replaceFileState(file.path, newState);
|
||||
resolve({ ...file, ...newState });
|
||||
} catch (e) {
|
||||
setFileState(file.path, "uploaded", 1);
|
||||
console.error("Error parsing response", e);
|
||||
@@ -496,9 +498,9 @@ const Preview: React.FC<PreviewProps> = ({ file, handleUpload, handleDelete }) =
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-col px-1.5 py-1">
|
||||
<p className="truncate">{file.name}</p>
|
||||
<p className="truncate select-text">{file.name}</p>
|
||||
<div className="flex flex-row justify-between text-sm font-mono opacity-50 text-nowrap gap-2">
|
||||
<span className="truncate">{file.type}</span>
|
||||
<span className="truncate select-text">{file.type}</span>
|
||||
<span>{(file.size / 1024).toFixed(1)} KB</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -33,6 +33,7 @@ let routesShown = false;
|
||||
export default {
|
||||
async fetch(request: Request) {
|
||||
if (!app || recreate) {
|
||||
console.log("[DB]", credentials);
|
||||
app = App.create({
|
||||
connection: credentials,
|
||||
initialConfig
|
||||
|
||||
Reference in New Issue
Block a user