Is it possible to use Socket.io with NuxtJs? - socket.io

I want to use socket.io in my Nuxtjs. Is it possible?
I tried this tutorial but I am getting the following error:
These dependencies were not found:
* fs in ./node_modules/socket.io/lib/index.js
* uws in ./node_modules/engine.io/lib/server.js

The better way to play with Nuxt.js + Socket.io is to follow this official example from core-team: https://github.com/nuxt/nuxt.js/tree/dev/examples/with-sockets

Updated answer with linked example on GitHub
I would suggest to use the nuxt-socket-io module. It is really easy to set up and has a nice documentation.
I built this litte demo example and I will list the steps that I took to build it (this is even a bit more thorough than the Setup section of the npm package):
Add nuxt-socket-io dependency to your project:
yarn add nuxt-socket-io # or npm install nuxt-socket-io
(If you already have a socket.io server you can skip this part)
Add following line to your nuxt.config.js file: serverMiddleware: [ "~/serverMiddleware/socket-io-server.js" ] (Please do not mix up serverMiddleware with middleware, this are two different things)
Then, create the file ./serverMiddleware/socket-io-server.js where you can implement your socket.io server.
// This file is executed once when the server is started
// Setup a socket.io server on port 3001 that has CORS disabled
// (do not set this to port 3000 as port 3000 is where
// the nuxt dev server serves your nuxt application)
const io = require("socket.io")(3001, {
cors: {
// No CORS at all
origin: '*',
}
});
var i = 0;
// Broadcast "tick" event every second
// Or do whatever you want with io ;)
setInterval(() => {
i++;
io.emit("tick", i);
}, 1000);
// Since we are a serverMiddleware, we have to return a handler,
// even if this it does nothing
export default function (req, res, next) {
next()
}
(If you already have Vuex set up, you can skip this)
Add following empty Vuex store, i.e., create the file ./store/index.js, since the module needs Vuex set up.
export const state = () => ({})
Add nuxt-socket-io to the modules section of nuxt.config.js, this will enable socket-io client:
{
modules: [
'nuxt-socket-io',
],
// socket.io configuration
io: {
// we could have multiple sockets that we identify with names
// one of these sockets may have set "default" to true
sockets: [{
default: true, // make this the default socket
name: 'main', // give it a name that we can later use to choose this socket in the .vue file
url: 'http://localhost:3001' // URL wherever your socket IO server runs
}]
},
}
Use it in your components:
{
data() {
return {
latestTickId: 0,
};
},
mounted() {
const vm = this;
// use "main" socket defined in nuxt.config.js
vm.socket = this.$nuxtSocket({
name: "main" // select "main" socket from nuxt.config.js - we could also skip this because "main" is the default socket
});
vm.socket.on("tick", (tickId) => {
vm.latestTickId = tickId;
});
},
}
Run it with npm run dev and enjoy your tick events :)

Nuxt + socket.io
For me worked:
Create project as nodejs app (not static page);
Install socket.io npm i socket.io;
Add serverMiddleware section to nuxt.config.js:
export default {
...,
serverMiddleware: [
{path: '/ws', handler: '~/api/srv.js'},
],
}
Create middleware /app/srv.js:
const app = require('express')()
const socket = require('socket.io')
let server = null
let io = null
app.all('/init', (req, res) => {
if (!server) {
server = res.connection.server
io = socket(server)
io.on('connection', function (socket) {
console.log('Made socket connection');
socket.on('msg', msg => {
console.log('Recived: ' + msg)
setTimeout(() => {
socket.emit('msg', `Response to: ${msg}`)
}, 1000)
})
socket.on('disconnect', () => console.log('disconnected'))
})
}
res.json({ msg: 'server is set' })
})
module.exports = app
Socket.io needs server which is not created in middleware, that's why is taken from firest request to app from res.connection.server.
Create page pages/index.vue:
<template>
<div class="container">
<input v-model="msg">
<button #click="socket.emit('msg', msg)">send</button>
<br/>
<textarea v-model="resps"></textarea>
</div>
</template>
<script>
export default {
head: {
script: [
{src: 'https://cdnjs.cloudflare.com/ajax/libs/socket.io/3.0.4/socket.io.js'},
],
},
data () {
return {
socket: null,
msg: 'wwJd',
resps: '',
}
},
mounted () {
this.$axios.$get('/ws/init')
.then(resp => {
this.socket = io()
this.socket.on('msg', msg => this.resps += `${msg}\n`)
})
},
}
</script>
Run it npm run dev;
Modify and enjoy :-)

Related

NextAuth getSession from subdomain not working

I am building a platform that offers different applications, the main platform is running on http://localhost and the applications will run each on a specific subdomain, at the moment I have an application running on http://sub.localhost.
I am using Nginx and Docker to host both the platform and the application, my goal would be to authenticate on http://localhost and use the session of the platform in the applications (subdomains), I have already taken a look at every single source/similar problem but could not find a solution, some of the sources I have read are the following:
https://github.com/nextauthjs/next-auth/discussions/1299
https://github.com/nextauthjs/next-auth/issues/405
https://github.com/nextauthjs/next-auth/issues/2718
At the moment this is my .env.local on the main platform:
NODE_ENV=development
GOOGLE_CLIENT_ID=...
GOOGLE_CLIENT_SECRET=...
GOOGLE_AUTH_URL=...
NEXTAUTH_URL=http://localhost/
NEXTAUTH_URL_INTERNAL=http://mygames:3000/
NEXTAUTH_SECRET=...
DATABASE_URL=...
NEXT_PUBLIC_API_KEY=...
NEXT_SECRET_API_KEY=...
The following is the .env.local of the application (subdomain):
NEXTAUTH_URL=http://sub.localhost/
NEXTAUTH_URL_INTERNAL=http://mygames:3000/
NEXTAUTH_SECRET=...
DATABASE_URL=...
NEXT_PUBLIC_API_KEY=...
NEXT_SECRET_API_KEY=...
The following is my [...nextauth].js for the main platform:
import NextAuth from 'next-auth';
import GoogleProvider from 'next-auth/providers/google';
import { PrismaAdapter } from '#next-auth/prisma-adapter';
import prisma from '../../../lib/prisma';
import Stripe from 'stripe';
const getDomainWithoutSubdomain = url => {
const urlParts = new URL(url).hostname.split('.');
return urlParts
.slice(0)
.slice(-(urlParts.length === 4 ? 3 : 2))
.join('.');
};
const hostName = getDomainWithoutSubdomain(process.env.NEXTAUTH_URL);
console.log("HOSTNAME", hostName);
const options = {
secret: process.env.NEXTAUTH_SECRET,
adapter: PrismaAdapter(prisma),
providers: [
GoogleProvider({
clientId: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
}),
],
pages: {
signIn: '/signin'
},
callbacks: {
async signIn({ user, account, profile, email, credentials }) {
return true;
},
async redirect({ url, baseUrl }) {
return baseUrl;
},
async session({ session, user, token }) {
return { ...session, ...user };
},
async jwt({ token, user, account, profile, isNewUser }) {
return token;
}
},
cookies: {
sessionToken: {
name: process.env.NODE_ENV === 'production' ? `__Secure-next-auth.session-token` : 'next-auth.session-token',
options: {
httpOnly: true,
sameSite: 'lax',
path: '/',
secure: process.env.NODE_ENV === 'production' ? true: false,
domain: '.' + hostName
}
}
}
}
export default (req, res) => NextAuth(req, res, options)
When I use getSession in the subdomain application I receive a null object, what can I do to solve this? Feel free to ask for anything for more details!
Spent ages looking for a solution...
Solution:
https://github.com/nextauthjs/next-auth/discussions/4089#discussioncomment-2290660
TLDR; You cannot use localhost subdomains as intended. You must use example.com and app.example.com. To set these go to the hosts file in you system.
Follow the steps in the github post if needed

There is no matching message handler error in NestJs TCP E2E test

I'm playing around with Microservice architecture using NestJs. I've made a simplified repository with a few services that communicate over TCP with a mix of message and event patterns.
I have moved on to writing E2E tests for the using Supertest, and while I'm able to run the needed microservice, the requests respond with {"error": "There is no matching message handler defined in the remote service.", "statusCode": 500}
GatewayService: HTTP Rest Api where the E2E tests are run. Calls the service
AuthService: NestJs microservice running on 0.0.0.0:3001 by default
configService: a simple service that returns information needed to set up the services, like host and port. I have tried eliminating it from the test and hardcoding the values.
The E2E test file
import { INestApplication, ValidationPipe } from '#nestjs/common';
import { ClientProxy, ClientsModule, Transport } from '#nestjs/microservices';
import { Test, TestingModule } from '#nestjs/testing';
import * as request from 'supertest';
import { configService } from '../src/config.service';
import { RpcExceptionFilter } from '../src/filters/rpc-exception.filter';
import { AppModule } from './../src/app.module';
describe('AuthenticationController (e2e)', () => {
let app: INestApplication;
let authClient: ClientProxy;
beforeAll(async () => {
const moduleFixture: TestingModule = await Test.createTestingModule({
imports: [
AppModule,
ClientsModule.register([
{
...configService.getServiceConfigs().authService,
transport: Transport.TCP,
},
]),
],
}).compile();
// Setup the app instance
app = moduleFixture.createNestApplication();
// Setup the relevant micorservice(s)
app.connectMicroservice({
transport: Transport.TCP,
name: configService.getServiceConfigs().authService.name,
options: configService.getServiceConfigs().authService.options,
});
app.startAllMicroservices();
// Add request validation
app.useGlobalPipes(
new ValidationPipe({
transform: true,
whitelist: true,
forbidNonWhitelisted: true,
forbidUnknownValues: true,
}),
);
// Add needed filters
app.useGlobalFilters(new RpcExceptionFilter());
await app.init();
authClient = app.get(configService.getServiceConfigs().authService.name);
await authClient.connect();
console.log('authClient', authClient);
});
describe('POST /auth/login', () => {
it('Should return status 200 and a user object with access token', () => {
return (
request(app.getHttpServer())
.post('/auth/login')
.send({ username: 'exmple#user.com', password: 'password' })
// .expect(200)
.expect((response) => {
console.log('response', response.body);
expect(response.body).toHaveProperty('id');
expect(response.body).toHaveProperty('username');
expect(response.body).toHaveProperty('accessToken');
})
);
});
});
afterAll(async () => {
await app.close();
await authClient.close();
});
});
I have attempted adding a provider which I've used before when working with Grpc as the transport layer (this is TCP). Didn't change anything.
beforeAll(async () => {
const moduleFixture: TestingModule = await Test.createTestingModule({
...
providers: [
{
provide: 'AUTH_SERVICE',
useFactory: () => {
return ClientProxyFactory.create({
transport: Transport.TCP,
options: { host: 'localhost', port: 3001 },
});
},
},
],
I know that the microservice starts up and the gateway service is able to connect to it since when printing the authClient: Client proxy it returns a correct object with URL 0.0.0.0:3001. If I change the URL, or the name of the service in any part of the setup then errors about missing providers show, further confirming that it is supposedly correctly set up.
One of the best guides I've found on this matter. Sadly it doesn't work for my code.

ServiceWorkers and Next.js: How would one integrate Service workers in production with a next.js app?

I am working with "next": "^9.3.2" and integrated a service worker (including this just in case someone else has a similar question):
File structure:
pages
public
static
serviceWorker.js
server
index.js
In server/index.js
async function start() {
const dev = process.env.NODE_ENV !== 'production';
const app = nextJS({ dev });
const server = express();
....
server.get('/serviceWorker.js', (req, res) => {
res.sendFile(path.resolve(__dirname, '../public', 'serviceWorker.js'));
});
/* later */
if (process.env.NODE_ENV === 'production') {
server.use(express.static('.next/static'));
server.get('/service-worker.js', (req, res) => {
res.sendFile(path.resolve(__dirname, 'public', 'serviceWorker.js'));
});
In public/serviceWorker.js
var currentCaches = {
css: 'CSS',
images: 'images'
};
const cacheFiles = {
css: [
// 'http://localhost:8016/semantic-ui-css/semantic.min.css',
// 'http://localhost:8016/font-awesome/css/font-awesome.min.css',
// 'http://localhost:8016/leaflet/dist/leaflet.css',
// 'http://localhost:8016/esri-leaflet-geocoder/dist/esri-leaflet-geocoder.css',
// 'http://localhost:8016/styles/styles.css',
// 'http://localhost:8016/leaflet-routing-machine/dist/leaflet-routing-machine.css'
],
images: [
// 'https://raw.githubusercontent.com/pointhi/leaflet-color-markers/master/img/marker-icon-2x-green.png',
// 'https://raw.githubusercontent.com/pointhi/leaflet-color-markers/master/img/marker-icon-2x-red.png',
// 'https://cdnjs.cloudflare.com/ajax/libs/leaflet/0.7.7/images/marker-shadow.png',
// 'http://localhost:8016/public/static/media/search#2x.png',
// 'http://localhost:8016/public/static/uploads/profile-avatars/placeholder.jpg'
]
};
self.addEventListener('install', function(event) {
console.log('Hello world from the Service Worker 🤙');
event.waitUntil(
Promise.all([
caches.open(currentCaches.css).then(cache => {
return cache.addAll(cacheFiles.css);
}),
caches.open(currentCaches.images).then(cache => {
return cache.addAll(cacheFiles.images);
})
])
);
});
Right now I am declaring the css paths in that object above* like I would in my _app.js file:
import 'semantic-ui-css/semantic.min.css';
import 'font-awesome/css/font-awesome.min.css';
import 'leaflet/dist/leaflet.css';
import 'esri-leaflet-geocoder/dist/esri-leaflet-geocoder.css';
import '../styles/styles.scss';
import 'leaflet-routing-machine/dist/leaflet-routing-machine.css';
Figure this would apply to the images too
So my question is since next.js spits out a static/css on a production build:
.next
cache
server
static
chunks
css < -----
476a94f2.d9a9e468.chunk.css
dbd51271.19268786.chunk.css
styles.9ca4e15c.chunk.css
How would one specifically have the serviceWorker know what these file names would be (as well as images, fonts, svg's etc)? As i'm assuming the numbers are to help with caching!
Thanks!
Generally speaking, you need some way of integrating with your web app's build process if you want to get a list of hashed URLs for use within your service worker.
Given that you're using Next.js, a plugin like next-offline can help by doing two things:
Integrating with your build process to get a list of your hashed URLs.
Generating the entirety of your service worker for you, using workbox-precaching under the hood to ensure that your URLs are properly cached and kept up to date.
You can implement something similar yourself if you'd prefer not to use next-offline, but you'd need to use something like next-assets-manifest to obtain the list of hashed URLs, write your own service worker, and figure out how to inject those URLs into the service worker.

how to run vue app in the same domain with laravel sanctum for SPA

I need help in running my Vue spa in the same domain as my laravel app , when running "npm run serve" in terminal I think it's working but when I go to the browser it's refusing connection. I haven't done the backend which I will use sanctum for handling API. Has anybody here have the same project working on like me? love to make conversations to solve this.
Thanks in advance
here is the vue.config.js file
const path = require('path')
const webpack = require('webpack')
const createThemeColorReplacerPlugin = require('./config/plugin.config')
function resolve (dir) {
return path.join(__dirname, dir)
}
/**
* check production or preview(pro.loacg.com only)
* #returns {boolean}
*/
function isProd () {
return process.env.NODE_ENV === 'production'
}
const assetsCDN = {
css: [],
// https://unpkg.com/browse/vue#2.6.10/
js: [
'//cdn.jsdelivr.net/npm/vue#2.6.10/dist/vue.min.js',
'//cdn.jsdelivr.net/npm/vue-router#3.1.3/dist/vue-router.min.js',
'//cdn.jsdelivr.net/npm/vuex#3.1.1/dist/vuex.min.js',
'//cdn.jsdelivr.net/npm/axios#0.19.0/dist/axios.min.js'
]
}
// webpack build externals
const prodExternals = {
vue: 'Vue',
'vue-router': 'VueRouter',
vuex: 'Vuex',
axios: 'axios'
}
// vue.config.js
const vueConfig = {
configureWebpack: {
// webpack plugins
plugins: [
// Ignore all locale files of moment.js
new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/)
],
// if prod is on, add externals
externals: isProd() ? prodExternals : {}
},
chainWebpack: (config) => {
config.resolve.alias
.set('#$', resolve('src'))
const svgRule = config.module.rule('svg')
svgRule.uses.clear()
svgRule
.oneOf('inline')
.resourceQuery(/inline/)
.use('vue-svg-icon-loader')
.loader('vue-svg-icon-loader')
.end()
.end()
.oneOf('external')
.use('file-loader')
.loader('file-loader')
.options({
name: 'assets/[name].[hash:8].[ext]'
})
// if prod is on
// assets require on cdn
if (isProd()) {
config.plugin('html').tap(args => {
args[0].cdn = assetsCDN
return args
})
}
},
css: {
loaderOptions: {
less: {
modifyVars: {
// less vars,customize ant design theme
'primary-color': '#00B4E4',
// 'link-color': '#F5222D',
'border-radius-base': '4px'
},
javascriptEnabled: true
}
}
},
}
if (process.env.VUE_APP_PREVIEW === 'true') {
vueConfig.configureWebpack.plugins.push(createThemeColorReplacerPlugin())
}
module.exports = vueConfig
module.exports = {
devServer: {
host: 'app.paymate-ui.test'
}
}
If I understand you correctly, you want to use Laravel and Vue.js together in the same application folder?
Should be pretty easy then.
First off, build your application with Vue scaffolding for the frontend.
Then, make a route that redirects everything to a single controller method that returns a spa view. (Or use a closure)
In this view, include your app.js as an asset and include the main Vue component (something like <app></app>).
Then build your Vue app. All requests will now be forwarded to the spa view, which includes your app.js, which should bootstrap Vue.

How to cache using apollo-server

The apollo basic example at https://www.apollographql.com/docs/apollo-server/features/data-sources.html#Implementing-your-own-cache-backend they state that doing a redis cache is as simple as:
const { RedisCache } = require('apollo-server-cache-redis');
const server = new ApolloServer({
typeDefs,
resolvers,
cache: new RedisCache({
host: 'redis-server',
// Options are passed through to the Redis client
}),
dataSources: () => ({
moviesAPI: new MoviesAPI(),
}),
});
When I look at the examples of non-redis, it states that it's a simple { get, set } for cache. This means I should theoretically be able to do.
cache : {
get : function() {
console.log("GET!");
},
set : function() {
console.log("SET!");
}
}
No matter what I try, my cache functions are never called when I'm utilizing the graphQL explorer that apollo-server provides natively.
I have tried with cacheControl : true and with cacheControl set like it is in https://medium.com/brikl-engineering/serverless-graphql-cached-in-redis-with-apollo-server-2-0-f491695cac7f . Nothing.
Is there an example of how to implement basic caching in Apollo that does not utilize the paid Apollo Engine system?
You can look at the implementation of this package which caches the full response to implement your own cache.
import { RedisCache } from "apollo-server-redis";
import responseCachePlugin from "apollo-server-plugin-response-cache";
const server = new ApolloServer({
...
plugins: [responseCachePlugin()],
cache: new RedisCache({
connectTimeout: 5000,
reconnectOnError: function(err) {
Logger.error("Reconnect on error", err);
const targetError = "READONLY";
if (err.message.slice(0, targetError.length) === targetError) {
// Only reconnect when the error starts with "READONLY"
return true;
}
},
retryStrategy: function(times) {
Logger.error("Redis Retry", times);
if (times >= 3) {
return undefined;
}
return Math.min(times * 50, 2000);
},
socket_keepalive: false,
host: "localhost",
port: 6379,
password: "test"
}),
});
You should be able to use the NPM package 'apollo-server-caching' by implementing your own interface. See Implementing Your Own Cache which provides an example.

Resources