Closed rafaellucio closed 3 months ago
If you remove simple-stack-stream
do you still have the issue?
In this sample https://stackblitz.com/edit/github-ao77yt?file=src%2Fpages%2Findex.astro,src%2Fpages%2Fapi%2Flogin.ts
I don't use simple-stack-stream
and receive the similar error when submit data
And I remove simple-stack-stream
import { defineConfig } from 'astro/config';
import react from '@astrojs/react';
import tailwind from '@astrojs/tailwind';
import node from '@astrojs/node';
import { fileURLToPath } from 'node:url';
// import simpleStackStream from "simple-stack-stream";
// https://astro.build/config
export default defineConfig({
output: 'server',
adapter: node({
mode: 'standalone',
}),
outDir: './dist',
integrations: [
react(),
tailwind({
configFile: fileURLToPath(
new URL('./tailwind.config.mjs', import.meta.url),
),
}),
],
});
But receive de same error 😢
In this sample https://stackblitz.com/edit/github-ao77yt?file=src%2Fpages%2Findex.astro,src%2Fpages%2Fapi%2Flogin.ts
....
This error is very very strange, because when I run build and run locally
❯ node dist/server/entry.mjs
Works!!, But after deploy doesn't works
I use this actions to deploy my app in gcloud https://github.com/FirebaseExtended/action-hosting-deploy
@matthewp I identified the problem, when this action-hosting-deploy use the CLI to create an api function in Google Cloud Function this function use an library called firebase-frameworks
, this library haven't support to application/x-www-form-urlencoded
, when Astro run context.request.formData()
the formData
don't exists.
Basically add a simple form like this:
<form action="/api/test" method="POST">
<input name="user" />
<input name="pass" />
<button>submit</button>
</form>
When I use Astro the content type of the request context receives an application/x-www-form-urlencoded
and to obtain these values I need to parse my request to FormData using request.formData()
as request.json()
and added a new method in the request context called formData
My workarround was create an Astro middleware and use the busboy library to obtain values from request, like this
import { defineMiddleware } from 'astro:middleware';
import Busboy from 'busboy';
const getFieldsFromFormData = (headers: any, body: any) =>
new Promise(async (resolve) => {
const busboy = Busboy({ headers });
let fields: any = {};
busboy.on('field', (field: string, val: any) => {
fields = JSON.parse(field);
});
busboy.on('finish', () => resolve(fields));
busboy.end(body);
});
export const formBody = defineMiddleware(async (context, next) => {
const req = context.request.clone();
const headers = Object.fromEntries(req.headers);
if (
req.method === 'POST' &&
req.headers.get('content-type') === 'application/x-www-form-urlencoded'
) {
try {
const text = await req.text();
const fields: any = await getFieldsFromFormData(headers, text);
context.request.formData = async function () {
return {
...fields,
get: (key: string) => fields[key] ?? '',
};
};
} catch (err) {
console.error(err);
}
}
return next();
});
This works but it's a really bad solution 😢
I look this library firebase-framework-tools/astro maybe can create an Github Actions, and use CLI to publish function using this library
Ah ok, x-www-form-urlencoded
is not the right content type for FormData. Instead you want to use URLSearchParams
like so let params = new URLSearchParams(await request.text())
.
@rafaellucio Please consider getting rid of firebase-frameworks
at all. I tried many ways and always something doesn't work as expected, especially environment variables and form data.
My suggestion: create Dockerfile as described here: https://docs.astro.build/en/recipes/docker/#ssr, deploy to Google Cloud Run and don't use Google Functions at all. If you don't believe me, go and take a look at your functions' source code - firebase-frameworks
has made a nice spaghetti out of it.
as @orkisz mentioned it, better try external service.
I change my forms instead use x-www-form...
change it to json/application
but on image processing that does not work.
const data = await request.formData();
const file = data.get("file") as File;
const promoName = data.get("name");
if (!file) {
return new Response(JSON.stringify({ error: "No file uploaded" }), {
status: 400,
headers: { "Content-Type": "application/json" },
});
}
const auth = getAuth(app);
const sessionCookie = cookies.get("__session")?.value || '';
const authorized = await auth.verifySessionCookie(sessionCookie);
if(!authorized) {
return new Response(
JSON.stringify({
message: "Cookie No valida!",
}),
{ status: 403 }
);
}
const imageReader = file.stream().getReader();
let receivedLength = 0;
let chunks = [];
while (true) {
const { done, value } = await imageReader.read();
if (done) {
break;
}
chunks.push(value);
receivedLength += value.length;
}
const combinedChunks = Buffer.concat(chunks, receivedLength);
const newFileName = new Date().getMilliseconds() + file.name;
await storage.bucket('orthodent-center.appspot.com')
.file('promos/' + newFileName)
.save(combinedChunks);
waste of time, works locally but does not work at all on Firebase Servers
Astro Info
If this issue only occurs in one browser, which browser is a problem?
No response
Describe the Bug
I receibe this message when I deploy my app
[ERROR] TypeError: Error: Unexpected end of multipart data
, following this documentation https://docs.astro.build/en/recipes/build-forms-api/#recipeToday I use the standalone node setup in astro.config.mjs
This is my API test
After call GET this works, but post doesn't works
The log em Google Cloud Function is:
[ERROR] TypeError: Error: Unexpected end of multipart data
What's the expected result?
Link to Minimal Reproducible Example
https://stackblitz.com/edit/github-ao77yt?file=src%2Fpages%2Findex.astro,src%2Fpages%2Fapi%2Flogin.ts,package.json
Participation