The firebase Sveltekit client app and server api use a google cloud run hosting container. This works fine when I use the cloud run url: https://app...-4ysldefc4nq-uc.a.run.app/
But when I use firebase rewriting the client works fine using: https://vc-ticker.web.app/...
but receives 502 and 504 responses from the API service. The cloud run log does not show any errors, receives the client fetch POST request and returns a Readablestream response.
But this API service response stream never arrives when using rewrites.
firebase.json
{
"hosting": {
"public": "public", !! NOT used, cloud run hosts the app
"ignore": [
"firebase.json",
"**/.*",
"**/node_modules/**"
],
"rewrites": [
{
"source": "**",
"run": {
"serviceId": "vc-ticker-app",
"region": "us-central1"
}
}
]
}
}
page.svelte client API request:
const logging = true;
const controller = new AbortController();
let reader = null;
const signal = controller.signal;
async function streamer(params) {
console.log("stream with logging:", logging, JSON.stringify(params));
try {
const response = await fetch("api/my-ticker", {
method: "POST",
body: JSON.stringify(params),
headers: {
"content-type": "application/json",
},
signal: signal,
});
const stream = response.body.pipeThrough(new TextDecoderStream("utf-8"));
reader = stream.getReader();
while (true) {
const { value, done } = await reader.read();
if (done || response.status !== 200) {
console.log("done response", response.status, done, value);
await reader.cancel(`reader done or invalid response: ${response.status}`);
reader = null;
break;
}
// response ok: parse multi json chunks => array => set store
const quotes = {};
JSON.parse(`[${value.replaceAll("}{", "},{")}]`).forEach((each, idx) => {
quotes[each.id] = [each.price, each.changePercent];
console.log(`quote-${idx}:`, quotes[each.id]);
});
positions.set(quotes);
}
} catch (err) {
console.log("streamer exception", err.name, err);
if (reader) {
await reader.cancel(`client exception: ${err.name}`);
reader = null;
}
}
}
$: if ($portfolio?.coins) {
const params = {
logging,
symbols: Object.values($portfolio.symbols),
};
streamer(params);
}
onDestroy(async () => {
if (reader) await reader.cancel("client destroyed");
controller.abort();
console.log("finished");
});
I use the Sveltekit adapter-node to build the app.
CodePudding user response:
With rewrite rules, you can direct requests that match specific patterns to a single destination.Check your firebase.json file and verify if the rewrite configuration in the hosting section has the redirect serviceId name same as that from the deployed container image,as per below example
"hosting": {// ...
// Add the "rewrites" attribute within "hosting"
"rewrites": [ {
"source": "/helloworld",
"run": {
"serviceId": "helloworld", // "service name" (from when you [deployed the container image][3])
"region": "us-central1" // optional (if omitted, default is us-central1)
}
} ]
}
It is important to note that Firebase Hosting is subject to a 60-second request timeout. If your app requires more than 60 seconds to run, you'll receive an HTTPS status code 504 (request timeout). To support dynamic content that requires longer compute time, consider using an App Engine flexible environment.
You should also check the Hosting configuration page for more details about rewrite rules. You can also learn about the priority order of responses for various Hosting configurations.
CodePudding user response:
I made it work with an external link to the cloud run api service (cors). But I still do not understand why It can't be done without cors using only firebase rewrites.
page.svelte client API request update:
Now using GET and an auth token to verify the api request on the endpoint server
const search = new URLSearchParams(params);
const apiHost = "https://fs-por....-app-4y...q-uc.a.run.app/api/yahoo-finance-streamer";
const response = await fetch(`${apiHost}?${search.toString()}`, {
method: "GET",
headers: {
"auth-token": await getIdToken(),
},
signal: signal,
});
And a handle hook to verify the auth token and handle cors:
const logging = true;
const reqUnauthorized = { status: 403, statusText: 'Unauthorized!' };
/** @type {import('@sveltejs/kit').Handle} */
export async function handle({ event, resolve }) {
let response;
if (event.request.method !== "OPTIONS") {
if (event.url.pathname.startsWith('/api')) {
const authToken = event.request.headers.get("auth-token")
const obj = await decodeIdToken(logging, authToken)
if ("message" in obj) return new Response(obj.message, reqUnauthorized);
if (verifyUser(logging, obj.uid) === false) {
return new Response(`user auth failed for: ${obj.email}`, reqUnauthorized);
}
}
response = await resolve(event);
} else { // handle cors preflight OPTIONS
response = new Response("", { status: 200 });
}
response.headers.append('Access-Control-Allow-Headers', "*");
response.headers.append('Access-Control-Allow-Origin', "*");
return response;
}