I'm trying to utilize a socket.io connection with firebase hosting/functions but I'm running into a few problems. It started off as a CORS issue (which im sure is still the problem) but now I'm just totally lost on whats wrong, below is my firebase.json, index.js (firebase function file), and even the angular client file which initializes the connection to the server.
firebase.json
{
"hosting": {
"public": "public",
"ignore": [
"firebase.json",
"**/.*",
"**/node_modules/**"
],
"headers": [
{
"source" : "**",
"headers" : [ {
"key" : "Access-Control-Allow-Headers",
"value" : "Origin"
} ]
},
{
"source" : "**",
"headers" : [ {
"key" : "Access-Control-Allow-Origin",
"value" : "http://localhost:4200"
} ]
}
],
"rewrites": [
{
"source": "**",
"function": "app"
}
]
},
"functions": {
"predeploy": [
"npm --prefix \"$RESOURCE_DIR\" run lint"
],
"source": "functions"
}
}
index.js
const functions = require('firebase-functions');
const express = require('express');
var app = express();
const http = require('http').Server(express);
const socketio = require('socket.io')(http);
const cors = require('cors');
app.use(cors({credentials: true, origin: '*:*'}));
app.get('/tester', (request, response) => {
//response.header('Access-Control-Allow-Origin', '*');
response.send('Hello!');
socketio.on('connection', socket => {
connections.push(socket);
console.log('New client connected ('+connections.length+' connections).');
//console.log(socket);
socket.emit('port', 'LIVE SHIT');
});
})
exports.app = functions.https.onRequest(app)
app.component.ts (client component for connection)
import { Component } from '#angular/core';
import io from 'socket.io-client';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent {
title = 'multi-client';
port: number = 0;
socket: any;
width: number = 100;
updateVal: number = 5;
constructor()
{
this.socket = io('http://localhost:5000/tester');
this.socket.on('port', port => {
this.port = port;
})
}
updateWidth(val)
{
this.width += val;
}
}
This is the error I'm getting.
I viewed a few posts on stackoverflow and various other sites that had similar problems but nothing seemed to work. I'm sure im doing it wrong but I'm lost on what I'm missing to accomplish this. Please help!
Cloud Functions are for relatively short-lived operations with a clear end. You cannot use Cloud Functions to keep a connection to the client open.
The reason is that Cloud Functions closes the resources of your container by the time it thinks you're done. In the case of a HTTP function like yours, that means that these resources are gone when you've called response.send('Hello!');.
So what is possible is to send the response to the client once you've established a socket connection like this:
app.get('/tester', (request, response) => {
socketio.on('connection', socket => {
connections.push(socket);
console.log('New client connected ('+connections.length+' connections).');
//console.log(socket);
socket.emit('port', 'LIVE SHIT');
response.send('Hello!');
});
})
But in that case too, the connection will be closed after the call to response.send('Hello!');. And while you could not send a response, even in that case the connection/resources will be closed after 9 minutes, since that is the maximum time a Cloud Function can run.
It all goes back to my initial statement that Cloud Functions are only for short-lived operations with a clear end moment. For long-running processes, use another platform, such as App Engine, Compute Engine, or one of the many other offerings where you can manage your own processes.
Also see:
Run a web socket on Cloud Functions for Firebase?
Google Cloud Functions with socket.io
Related
I have a Nuxt3 app that is using "server routes" to create backend APIs to use for the front-end.
I have the following server route:
server/api/imagekit/deleteFile.js:
import ImageKit from 'imagekit'
const imagekit = new ImageKit({
publicKey: useRuntimeConfig().public.imagekitPublicKey,
privateKey: useRuntimeConfig().imagekitPrivateKey,
urlEndpoint: useRuntimeConfig().public.imagekitBaseURL
})
export default defineEventHandler(async (event) => {
// Purge cache of file from Imagekit
// See detailed email from Rahul # imagekit dated aug 31, 2022
const body = await useBody(event)
const response = await imagekit.purgeCache(body.url)
return response
})
The above code works fine locally, but once I deploy to Firebase Hosting, I get the following server error when trying to access the API deleteFile:
description: ""
message: "Missing privateKey during ImageKit initialization"
statusCode: 500
statusMessage: "Internal Server Error"
url: "/api/imagekit/deleteFile"
In case it's relevant to this question, here is my code for Nuxt's nuxt.config.ts file where the runtimeConfig property is listed:
runtimeConfig: {
imagekitPrivateKey: '',
public: {
baseURL: process.env.NODE_ENV === 'production' ? 'https://example.com' : 'http://localhost:3000',
imagekitBaseURL: 'https://ik.imagekit.io/example/',
imagekitPublicKey: 'public_AdZM6u2+FvznG/LngYp7Ab3TJy4='
}
}
Also, my firebase.json uses 2 codebases for the functions: one for server and one for cloud functions:
{
"functions": [
{
"source": ".output/server",
"codebase": "nuxt"
},
{
"source": "functions",
"codebase": "functions"
}
],
"hosting": [
{
"site": "XXX",
"public": ".output/public",
"ignore": ["firebase.json", "**/.*", "**/node_modules/**"],
"cleanUrls": true,
"rewrites": [
{
"source": "**",
"function": "server"
}
]
}
]
}
I do have an .env file in project root that holds the imagekitPrivateKey value.
How would I provide this information to Firebase hosting deployment so ImageKit properly initializes with the private key?
You can read the variables from .env in nuxt.config.ts as shown below:
export default defineNuxtConfig({
runtimeConfig: {
// Uppercase preferred in .env file
imagekitPrivateKey: process.env.IMAGEKIT_PRIVATE_KEY,
},
});
Then you can access it in your API routes:
export default defineEventHandler((event) => {
const { imagekitPrivateKey } = useRuntimeConfig();
return { message: "success" };
});
This might seem like a repeated question but i promise I've made sure
to check every related ones but non solve my problem
I am deploying a nextjs app on Firebase hosting using cloud functions,
the app does deploy and gives me back the hosting URL of app
link. visiting the URL i got this 403 forbidden error;
Your client does not have permission to get URL /nextServer/ from this server.
Which I fixed by adding allUsers and allAuthenticatedUsers as function invoker at function permission.
But then, after fixing that I got another error saying:
Error: could not handle the request
this terminated the function with a log of
Function execution took 804 ms, finished with status: 'crash'
This doesn't give any reason why the function crashed which has made fixing it all the more harder. Below is my nextServer function code:
const { https } = require("firebase-functions");
const { default: next } = require("next");
const isDev = process.env.NODE_ENV !== "production";
const server = next({
dev: isDev,
conf: { distDir: ".next" },
});
const nextjsHandle = server.getRequestHandler();
exports.nextServer = https.onRequest((req, res) => {
return server.prepare().then(() => {
return nextjsHandle(req, res);
});
});
and this is the firebase.json:
{
"hosting": {
"public": "public",
"site": "webavocat",
"ignore": [
"firebase.json",
"**/.*",
"**/node_modules/**"
],
"rewrites": [
{
"source": "**",
"function": "nextServer"
}
]
},
"functions": {
"source": ".",
"runtime": "nodejs16",
"ignore": [
"**/.vscode/**",
".firebase/**",
".firebaserc",
"firebase.json",
"**/node_modules/**",
"**/public/**",
"**/.next/cache/**"
]
}
}
The project folder structure is as shown below
Edit 1:
Don't really know if this is correct,
const nextjsHandle = server.getRequestHandler();
exports.nextServer = https.onRequest((req, res) => {
try {
return server.prepare().then(() => {
return nextjsHandle(req, res);
});
} catch (error) {
console.error(error);
}
});
but i used a try catch on the on the server function as #mdobrucki suggested. still no log on the why the function crashed though
Trying to add a Custom Handler to a simple AZ Function project. AZF works ok locally at VSC before adding. After adding, F5 starts ok like before:
[2021-12-30T19:33:14.402Z] Startup operation 'a9550626-ee3e-1234-b254-9facc08a3890' completed.
Functions:
select: [GET,POST] http://localhost:7071/api/select
then:
For detailed output, run func with --verbose flag.
[2021-12-30T19:33:16.324Z] Waiting for HttpWorker to be initialized.
Request to: http://127.0.0.1:49774/ failing with exception message: No
connection could be made because the target machine actively refused
it. (127.0.0.1:49774)
The port is random, next time can be 62974, then F5 stops itself.
Here is what's been added:
customHandler into root host.json.
a new folder middleware contains
app.js, myroute.js, host.json, mdw.js
root host.json:
{
...
"customHandler": {
"description": {
"defaultExecutablePath": "node",
"defaultWorkerPath": "middleware/app.js"
},
"enableForwardingHttpRequest": true
}
}
middleware/app.js:
const express = require('express')
const app = express()
const port = 3005;
app.listen(port, ()=>{console.log('================ My mdw is on 3005 ================');});
require("./myroute.js")(app);
middleware/myroute.js:
const express = require('express')
const mdw = require("./mdw");
module.exports = app =>
{
app.post("/api/testmdw", mdw.mytest);
};
middleware/mdw.js:
async function mytest(req, res, next)
{
const q = req;
return res.json({mdw: "ok"});
}
module.exports = { mytest }
middleware/host.json:
{
"version": "2.0",
"extensionBundle": {
"id": "Microsoft.Azure.Functions.ExtensionBundle",
"version": "[2.*, 3.0.0)"
}
}
I am following this Blog to use middleware on azure function via custom handler
Host.json
"customHandler": {
"description": {
"defaultExecutablePath": "node",
"defaultWorkerPath": "azexpresstest/app.js"
},
"enableForwardingHttpRequest": true,
},
"extensions": {"http": {"routePrefix": ""}}
The customHandler section points to a target as defined by the defaultExecutablePath. The execution target may either be a command, executable, or file where the web server is implemented.
"customHandler": {
"description": {
"defaultExecutablePath": "app/handler.exe",
"workingDirectory": "app"
}
…
Refer here for more information
Curious what others are doing with SvelteKit adapter-node builds to put them into production.
For example...
Serving pre-compressed files
Setting a cache TTL
Maybe something like helmet
Is it better to define an entryPoint for the adapter like a server.js that implements polka/express/connect like this...
// src/server.js
import { assetsMiddleware, prerenderedMiddleware, kitMiddleware } from '../build/middlewares.js'
import polka from 'polka'
import compression from 'compression'
import helmet from 'helmet'
const app = polka()
app.use(helmet())
app.use(assetsMiddleware, prerenderedMiddleware, kitMiddleware)
app.use(compression())
app.listen(3000)
or is it better to implement similar functionality in the handler() method of hooks.js?
Interested to know what people are doing to go from a build via adapter-node to production.
After examining what adapter-node generates in the build folder, I decided to set the entryPoint property for the adapter's options in svelte.config.js to ./src/server.mjs which gets added to the build. The handle() method in hooks.js/ts doesn't allow for any control over the static content.
In the code below, I set a redirect for non-https and use helmet to beef up security.
// /src/server.mjs
import polka from 'polka'
import helmet from 'helmet'
import { assetsMiddleware, prerenderedMiddleware, kitMiddleware } from '../build/middlewares.js'
const { PORT = 3000, DOMAIN } = process.env
const isHttpPerHeroku = (req) =>
req.headers['x-forwarded-proto'] &&
req.headers['x-forwarded-proto'] !== 'https'
polka()
// On Heroku (only), redirect http to https
.use((req, res, next) => {
if (isHttpPerHeroku(req)) {
let url = `${DOMAIN}${req.url}`
let str = `Redirecting to ${url}`
res.writeHead(302, {
Location: url,
'Content-Type': 'text/plain',
'Content-Length': str.length
})
res.end(str)
} else next()
})
// Apply all but two helmet protections
.use(helmet({
contentSecurityPolicy: false, // override below
referrerPolicy: false // breaks "Sign in with Gooogle"
}))
// Set the Content Security Policy on top of defaults
.use(helmet.contentSecurityPolicy({
useDefaults: true,
directives: {
scriptSrc: [
"'self'",
`'unsafe-inline'`,
'https://accounts.google.com/gsi/',
'https://assets.braintreegateway.com/web/',
'https://platform.twitter.com/',
'https://www.google-analytics.com/',
'https://www.google.com/recaptcha/',
'https://www.googletagmanager.com/',
'https://www.gstatic.com/recaptcha/'
],
connectSrc: [
"'self'",
'https://accounts.google.com/gsi/',
'https://api.sandbox.braintreegateway.com/merchants/',
'https://api.braintreegateway.com/merchants/',
'https://origin-analytics-sand.sandbox.braintree-api.com/',
'https://payments.sandbox.braintree-api.com/',
'https://payments.braintree-api.com/',
'https://stats.g.doubleclick.net/',
'https://www.google-analytics.com/',
'https://platform.twitter.com/',
'https://assets.braintreegateway.com/web/',
'https://www.googletagmanager.com/',
'https://www.google.com/recaptcha/',
'https://www.gstatic.com/recaptcha/',
'https://fonts.gstatic.com/',
'https://client-analytics.braintreegateway.com/'
],
childSrc: [
"'self'",
'https://accounts.google.com/gsi/',
'https://assets.braintreegateway.com/web/',
'https://platform.twitter.com/',
'https://syndication.twitter.com/i/jot',
'https://www.google.com/maps/',
'https://www.google.com/recaptcha/'
],
fontSrc: [
"'self'",
'https:',
'data:',
'https://fonts.gstatic.com'
],
imgSrc: [
"'self'",
'data:',
'https://www.google-analytics.com/',
'https://www.googletagmanager.com/',
'www.w3.org/2000/svg',
],
frameSrc: [
'https://accounts.google.com/gsi/',
'https://www.google.com/recaptcha/',
'https://platform.twitter.com/',
'https://assets.braintreegateway.com/web/',
'https://www.google.com/maps/',
'https://syndication.twitter.com/i/jot'
],
workerSrc: [
"'self'"
]
}
}))
// Load the SvelteKit build
.use(assetsMiddleware, prerenderedMiddleware, kitMiddleware)
// Listen on the appropriate port
.listen(PORT)
I'm trying to connect to a remote node.js server using http://server:3000. When I query the server for data, I get no data back. I look at the browser debugger and see that it's looking localhost:3000/page, and obviously getting no data.
Part of app.module.ts
` providers: [DateRangeService, CategoryserviceService,
{ provide: APP_BASE_HREF, useFactory: getBaseUrl }]
// providers: [DateRangeService]
})
export class AppModule { }
export function getBaseUrl() {
return document.getElementsByTagName('base')[0].href;
}`
The calling code looks like this
constructor(http: Http, private drs: DateRangeService, #Inject(APP_BASE_HREF)
private baseurl: string) {
this.demoId = "ex1";
this.http = http;
}
...
this.http.get(this.baseurl + volumeURL, {
params: {
// // startdate: this.startdate.value,
startdate: sdate,
enddate: edate
I end-up with an error http://localhost:3000/volumes?startdate=2018-6-20&enddate=2018-7-4 that the data can't be reached. Or in other words, it's point to the localhost instead of the server that is serving the pages.
Thanks
You can use a proxy setting for the service URLs
proxy.cofig.json
{
"/rest": {
"target": "http://server:3000/",
"secure": false
},
"/other": {
"target": "http://server:8060/",
"secure": false
}
}
Then serve using proxy-config
ng serve --proxy-config proxy.config.json