mirror of
https://gitlab.com/soapbox-pub/ditto.git
synced 2025-12-06 03:19:46 +00:00
Add a custom parseFormData helper to simulate Mastodon's (Rails) formdata parser
This commit is contained in:
parent
281f57d88e
commit
098e1b7fff
3 changed files with 79 additions and 2 deletions
|
|
@ -2,7 +2,6 @@ import { type Context } from '@hono/hono';
|
|||
import { HTTPException } from '@hono/hono/http-exception';
|
||||
import { NostrEvent, NostrFilter } from '@nostrify/nostrify';
|
||||
import Debug from '@soapbox/stickynotes/debug';
|
||||
import { parseFormData } from 'formdata-helper';
|
||||
import { EventTemplate } from 'nostr-tools';
|
||||
import * as TypeFest from 'type-fest';
|
||||
|
||||
|
|
@ -13,6 +12,7 @@ import { RelayError } from '@/RelayError.ts';
|
|||
import { AdminSigner } from '@/signers/AdminSigner.ts';
|
||||
import { Storages } from '@/storages.ts';
|
||||
import { nostrNow } from '@/utils.ts';
|
||||
import { parseFormData } from '@/utils/formdata.ts';
|
||||
import { purifyEvent } from '@/utils/purify.ts';
|
||||
|
||||
const debug = Debug('ditto:api');
|
||||
|
|
@ -182,7 +182,11 @@ async function parseBody(req: Request): Promise<unknown> {
|
|||
switch (req.headers.get('content-type')?.split(';')[0]) {
|
||||
case 'multipart/form-data':
|
||||
case 'application/x-www-form-urlencoded':
|
||||
return parseFormData(await req.formData());
|
||||
try {
|
||||
return parseFormData(await req.formData());
|
||||
} catch {
|
||||
throw new HTTPException(400, { message: 'Invalid form data' });
|
||||
}
|
||||
case 'application/json':
|
||||
return req.json();
|
||||
}
|
||||
|
|
|
|||
30
src/utils/formdata.test.ts
Normal file
30
src/utils/formdata.test.ts
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
import { assertEquals, assertThrows } from '@std/assert';
|
||||
|
||||
import { parseFormData } from '@/utils/formdata.ts';
|
||||
|
||||
Deno.test('parseFormData', () => {
|
||||
const formData = new FormData();
|
||||
|
||||
formData.append('foo', 'bar');
|
||||
formData.append('fields_attributes[0][name]', 'baz');
|
||||
formData.append('fields_attributes[0][value]', 'qux');
|
||||
formData.append('fields_attributes[1][name]', 'quux');
|
||||
formData.append('fields_attributes[1][value]', 'corge');
|
||||
|
||||
const result = parseFormData(formData);
|
||||
|
||||
assertEquals(result, {
|
||||
foo: 'bar',
|
||||
fields_attributes: [
|
||||
{ name: 'baz', value: 'qux' },
|
||||
{ name: 'quux', value: 'corge' },
|
||||
],
|
||||
});
|
||||
|
||||
assertThrows(() => {
|
||||
const formData = new FormData();
|
||||
formData.append('fields_attributes[1]', 'unexpected');
|
||||
formData.append('fields_attributes[1][extra]', 'extra_value');
|
||||
parseFormData(formData);
|
||||
});
|
||||
});
|
||||
43
src/utils/formdata.ts
Normal file
43
src/utils/formdata.ts
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
import { parseFormData as _parseFormData } from 'formdata-helper';
|
||||
|
||||
/** Parse formData into JSON, simulating the way Mastodon does it. */
|
||||
export function parseFormData(formData: FormData): unknown {
|
||||
const json = _parseFormData(formData);
|
||||
|
||||
const parsed: Record<string, unknown> = {};
|
||||
|
||||
for (const [key, value] of Object.entries(json)) {
|
||||
deepSet(parsed, key, value);
|
||||
}
|
||||
|
||||
return parsed;
|
||||
}
|
||||
|
||||
/** Deeply sets a value in an object based on a Rails-style nested key. */
|
||||
function deepSet(
|
||||
/** The target object to modify. */
|
||||
target: Record<string, any>,
|
||||
/** The Rails-style key (e.g., "fields_attributes[0][name]"). */
|
||||
key: string,
|
||||
/** The value to set. */
|
||||
value: any,
|
||||
): void {
|
||||
const keys = key.match(/[^[\]]+/g); // Extract keys like ["fields_attributes", "0", "name"]
|
||||
if (!keys) return;
|
||||
|
||||
let current = target;
|
||||
|
||||
keys.forEach((k, index) => {
|
||||
const isLast = index === keys.length - 1;
|
||||
|
||||
if (isLast) {
|
||||
current[k] = value; // Set the value at the final key
|
||||
} else {
|
||||
if (!current[k]) {
|
||||
// Determine if the next key is numeric, then create an array; otherwise, an object
|
||||
current[k] = /^\d+$/.test(keys[index + 1]) ? [] : {};
|
||||
}
|
||||
current = current[k];
|
||||
}
|
||||
});
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue