Merge remote-tracking branch 'origin/release/0.8.1' into release/0.8.1

# Conflicts:
#	app/src/ui/elements/media/Dropzone.tsx
This commit is contained in:
dswbx
2025-02-21 07:41:45 +01:00
9 changed files with 74 additions and 14 deletions

View File

@@ -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");

View File

@@ -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");
});
});

View File

@@ -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" }
);

View File

@@ -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);

View File

@@ -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;

View File

@@ -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 }> {

View File

@@ -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;

View File

@@ -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(".");
}

View File

@@ -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