Intro
Most of you will probably ask "Why?", why am I doing this stack? The reason is that initially I created the project in Nuxtjs + expressjs. But my PM wanted me to not distribute my source code to our client so I decided to bundle my code up into a single .exe file with electron. I tried using pkg but I couldn't figure out how to compile everything exactly.
The problem
The problem I am having with socket.io-client is that I want to be able to move the exe file to a different machine, and have socket.io connect to the socket.io server on that machine dynamically. Changing machines would mean that the IP of the server will be different, so whenever the user opens the webpage for that server, the socket.io-client would connect to the proper server. It works when I build the app from my current machine but when moved to lets say a VM then this is the response I get when I access the page:
ServiceUnavailableError: Response timeout
at IncomingMessage.<anonymous> (C:\Users\LIANG-~1\AppData\Local\Temp\nscAD47.tmp\app\resources\app.asar\node_modules\connect-timeout\index.js:84:8)
at IncomingMessage.emit (events.js:182:13)
at IncomingMessage.EventEmitter.emit (domain.js:442:20)
at Timeout._onTimeout (C:\Users\LIANG-~1\AppData\Local\Temp\nscAD47.tmp\app\resources\app.asar\node_modules\connect-timeout\index.js:49:11)
at ontimeout (timers.js:425:11)
at tryOnTimeout (timers.js:289:5)
at listOnTimeout (timers.js:252:5)
at Timer.processTimers (timers.js:212:10)
To further elaborate on what I am trying to do, lets say I compile the code on my current machine with the private IP of 192.168.0.104 (this runs perfectly), I want to move the exe file to another machine with the private IP of 192.168.0.105 (Accessing the webpage from this server gives the above error).
Technology used
The technology that I am using is nuxt.js created with express template, socket.io, and vue-socket.io-extended.
What I have tried
I have tried checking for reconnect events or timeout events, when these events are triggered then I call socket.connect(process.env.WS_URL) which doesn't work. I believe that when I packaged the electron app, it makes the plugin data immutable. I couldn't figure out someway to change the URL to the address of the machine.
import Vue from 'vue'
import io from 'socket.io-client'
import VueSocketIO from 'vue-socket.io-extended'
export default ({ store }) => {
// process.env.WS_URL = 'http://192.168.0.12:3000'
const socket = io(process.env.WS_URL, { transports: 'websocket' })
socket.on('timeout', () => {
socket.connect(process.env.WS_URL)
})
Vue.use(VueSocketIO, io(process.env.WS_URL, { transports: 'websocket' }), { store })
}
What I have right now
I created a socket.io plugin for nuxtjs to implement into my app, the plugin looks like this:
import Vue from 'vue'
import io from 'socket.io-client'
import VueSocketIO from 'vue-socket.io-extended'
export default ({ store }) => {
// process.env.WS_URL = 'http://192.168.0.12:3000'
Vue.use(VueSocketIO, io(process.env.WS_URL, { transports: 'websocket' }), { store })
}
I expect the socket.io-client to connect to the correct private IP of where the exe file is located. But Request Timeout is received, even though when I output the process.env.WS_URL is actually the new address.
EDIT: After further testing, seems like the socket.io plugin is fixed, after the build process. So changing the process.env.WS_URL wouldn't have any effect. Is there a way to change the URL for socket.io even after nuxtjs finished building?
(I wanted to comment this but I can't)
Anyway, are you sure you the information in the edit is true?
I use Nuxt.js with Electron and also the vue-socket.io. I use an argument for calling the executable as the socket.io address:
This is specific for electron
global.sharedObject = { socketIOAddress: process.argv[1] }
and this is my nuxt plugin file for vue-socket.io:
import Vue from 'vue'
import VueSocketIO from 'vue-socket.io'
import { remote } from 'electron'
export default ({ store }) => {
Vue.use(new VueSocketIO({
debug: true,
connection: remote.getGlobal('sharedObject').socketIOAddress
vuex: {
store,
actionPrefix: 'SOCKET_',
mutationPrefix: 'SOCKET_'
}
})
)
}
So while the code might not be exactly what you want Nuxt doesn't have to be build again for it. Maybe the environment variables didn't change right when you tried? You should try with a argument as well perhaps.
Also important: ssr: false in the nuxt config for the vue-socket.io plugin.
The solution I came up with is by using an environmental variable. First have the variable set as localhost:3000 or whatever your server's local address is. I would use electron-store to keep a config.json file of all my settings including the server IP. Before my server starts up, I read the config file and change the server address set previously. Then I use it as follows:
import Vue from 'vue'
import io from 'socket.io-client'
import VueSocketIO from 'vue-socket.io-extended'
export default ({ app, store }) => {
Vue.use(VueSocketIO, io(app.$env.WS_URL), { store })
}
NOTE: WS_URL is the environmental variable name.
Related
I installed an npm package, but since we can't use imports in nuxt 3, I don't get how to use it and couldn't find anything about this in the documentation. Does anyone know how to deal with this?
What kind of library you want to add to the project? Nuxt reads all files in your project and will import your imports inside of them. You need just pay attention, are library is made to be used in node.js or client browser. Exception to that are Nuxt modules you need to include in modules array inside nuxt.config files, but the intention of that is you won't need to import them in your project files for example.
Using the composable setup function, in the reality it is a simple async function that will run on a server and SSR HTML for client, so every thing you do directly there need to be safe to use in node.js.
Unless:
You will wrap component in <ClientOnly> component. Component won't be rendered on server.
You will use code in life cycle hook like onMounted(() => {...}).
You can paste it inside some function and not initiate it.
You will wrap code in your component in if(process.client) {...}.
Here is an example of plugin that runs on server and client.
import { defineNuxtPlugin } from '#app'
// Those imports are streight from node_modules installed
// using yarn add -D firebase or npm install -D firebase
// -D stands for devDependencies in package.json.
// You no need to install enything in "dependencies"
import { initializeApp, getApps } from '#firebase/app'
import { getAuth, onAuthStateChanged } from '#firebase/auth'
export default defineNuxtPlugin((nuxtApp) => {
const firebaseConfig = { ...useRuntimeConfig().public.firebaseConfig }
if (!getApps().length) initializeApp(firebaseConfig)
if(process.client) {
onAuthStateChanged(getAuth(), user => {
const currentUser = useState('user')
currentUser.value = user
})
}
}
Firebase is initialized on a server to be able to fetch data and SSR HTML files to a client. Then on client Firebase is initialized and it triggers onAuthStateChanges() function. This function initiate WebSocket connection with authentication system. It's in if(proces.client) so it won't trigger in node.js.
I am creating a chat application using socket.io along with vue js. My connection code looks like this:
import Vue from 'vue'
import VueSocketIO from 'vue-socket.io'
import SocketIO from "socket.io-client"
const io=SocketIO('https://example.com:8443');
Vue.use(new VueSocketIO({
debug: true,
connection: io
}))
There is no error in connection, which means the connection is done I think. And then I added on connection evening like this:
io.on('connection', socket => {
console.log("hai");
})
But this function is not calling when connection done. So to check I changed the port number, then the connection fails, so connection is done properly I think.
By referring to some other code, I just added like
sockets: {
connect: function() {
console.log('socket connected')
}
}
also.But didn't work.I am new to vue.How can i solve this.Thanks in advance
My console showing the follows
Vue-Socket.io: Received socket.io-client instance
vue-socketio.js?5132:10 Vue-Socket.io: Vuex adapter enabled
vue-socketio.js?5132:10 Vue-Socket.io: Vuex socket mutations disabled
vue-socketio.js?5132:10 Vue-Socket.io: Vuex socket actions enabled
vue-socketio.js?5132:10 Vue-Socket.io: Vue-Socket.io plugin enabled
I'm on Nuxt 2.15.8 and trying to build an offline app with electron.js and prisma+sqlite for local DB.
In nuxt to hit a local endpoint there is a common way of using serverMiddleware and express like this:
// api.js that will be added to nuxt.config.js file as serverMiddleware
import express from 'express'
const app = express()
app.use(express.json())
export default {
path: '/api',
handler: app
}
which send endpoints beginning with api/ through app handler which I can use to access my BD (the common way to access sqlite3 DB is the same)
// added to api.js
import { PrismaClient } from '../../resources/prisma/client'
const prisma = new PrismaClient()
app.get(`/user/info`, async (req, res) => {
const result = await prisma.user.findUnique({
where: {
id: 1,
},
})
console.console.log(res);
res.json(result)
})
this will work fine on nuxt, also fine on nuxt-electron dev mode. but on built exe file serverMiddleware won't be called. So as it has be done by others (nuxt-electron accessing offline local DB) there must be a way to define endpoints on client side. any idea??
Updated:
as I changed my Nuxt-Electron boilerplate I could access serverMiddleware in exe file but it wont hit the endpoints yet!
I am using jest and supertest to raise instance of express and run tests on it.
I faced with the problem of busy port that cannot still solve.
In my test I do the next:
import supertest from 'supertest';
const agent = supertest(app);
Then I go requests with agent and everything works fine.
Until I am running another test.
In app.js I have:
var app = express();
app.post('/auth/changePassword',VerifyToken, auth.changePassword);
app.listen(4001, function () {
console.log('Server is running');
});
So first spec runs perfect. But second tries to listen to the port that is already in use.
I really do not know how to close the connection here.
I tried app.close() but got error that such method. This is clear, that I have to assign
server = app.listen(4001, function () {
console.log('Server is running');
});
server.close();
But do not know how can I do that.
I also tried to preset agent in jest.setup and assign it to global variable
import app from "../../server/app";
import supertest from 'supertest';
const agent = supertest(app);
global.agent = agent;
But situation is the same, first test passed, second tries to raise express on the same port.
Supertest is able to start and stop your app for you - you should never need to explicitly kill the express server during the tests (even when running in parallel)
The problem is that in app.js your server is actually started - this means that when the app tests are run your server is started every time app.js is read (or once per test case)
You can remedy this by splitting the server start logic into a seperate file, so that importing app.js doesn't start the app (rather returns an express server instance) The pattern I normally use for this is:
// app.js
import express from 'express'
export const app = express();
app.get("/example", (req,res) => {
return res.status(200).json({data: "running"})
})
// server.js
import app from "./app"
app.listen(3000, () => console.log("listening on 3000"));
// app.spec.js
import app from "./app"
import request from "supertest"
it("should be running", async () => {
const result = await request(app).get("/example");
expect(result.body).toEqual({data: "running"});
});
I am new the Vue.js and I am trying first steps with an app. So for understanding the basics, I want a local config file per App installation to customise some needed variables in the code.
So in my main.js I tried the following:
import Vue from 'vue'
import App from './App.vue'
let config;
try {
config = require('../config.json');
} catch (e) {
config = require('../public/config.json');
}
Vue.config.productionTip = false;
Vue.prototype.$localConfig = config;
new Vue({
render: h => h(App)
}).$mount('#app');
This is working, until I build the production version with the dist folder. If I open the config.json in the root of the dist and change a property value, I see always the first defined values from the development env. So is webpack making there some caching? Is this at all the right way of handling such a local config file per App installation?
Maybe someone could give me some tips on this.
Doing config = require('../config.json'); is the same as import config from "../config.json" in a way that it takes the content of your json file at build time, transform it into JS object and make's it part of your app bundle.
You can do what you propose in a comment (include the file in a script tag in your index.html) but that means your app is doing additional request to the server to load the config and by doing so increasing "time to render" (time user have to wait until the page is fully rendered)
Most common way to handle app configuration in Vue/Webpack world is by using Environment Variables - those also "work" at build time tho so you need to build your app separately for each environment
let config
const configPromise =
process.env.NODE_ENV === 'development'
? import('../config.json')
: import('../public/config.json')
configPromise.then(res => {
config = res.default
})