Can only send data from the end which initiated the connection. How to send Data from other end (PeerJs) - webrtc

I am using the peerjs module in my SvelteKit app to create a peer-to-peer file transfer app
Also the custom signaling server using peerjs --port 5000 command and it's connecting successfully
Here's my code typescript from the file sender's end:
import { FormatFileSize } from '../lib/utils/FormatFileSize'; // function to convert file size into a string of kb, MB, GB file sizes
import Peer from 'peerjs';
import type { DataConnection } from 'peerjs'; // had to import separately because no export of DataConnection was shown from the 'peerjs' module I don't know why
import { onMount } from 'svelte';
export let file: FileList; // the file which needs to be transfered
let senderPeerId: string = '';
onMount(() => {
let peer: Peer = new Peer({
host: '/',
port: 5000
});
peer.on('open', function (id) {
console.log('My peer ID is: ' + id);
senderPeerId = id;
});
peer.on('connection', (dataConnection: DataConnection) => {
console.log('connected', dataConnection);
dataConnection.send('Hello'); // trying to send this but it's not being recie
dataConnection.on('data', function (data) {
console.log('Received', data); // I am logging the data received from the other end
});
});
});
And here's my code typescript from the file receiver's end (But this end is initiating the peer connection):
import type { PageData } from './$types';
import Peer from 'peerjs';
import { onMount } from 'svelte';
export let data: PageData; // in the formate of { receiverId: string } because this page params contain the peer id of the sender.
let peer: Peer; // initiating here so that can use it elsewhere
onMount(() => {
peer = new Peer({
host: '/',
port: 5000
});
peer.on('open', function (id) {
console.log('My peer ID is: ' + id);
startPeerConnection();
});
});
function startPeerConnection() {
const dataConnection = peer.connect(data.receiverId);
console.log(dataConnection);
dataConnection.on('open', function () {
console.log('connection started');
dataConnection.on('data', function (data) {
console.log('Received', data); // logging the data, but no data is recieved
});
dataConnection.send('World'); // this data is sent successfully and logged in the console of the other side
});
}
What am I doing wrong here, or only the side which initiates the connection can send data?
I looked through the internet but couldn't find a similar problem and a solution to it, please help !!

Related

Filecoin EVM : What is the possible solution to the error with code 1 when trying to look up actor state nonce on the Hyperspace network?

import '#ethersproject/shims';
import 'text-encoding';
import { ethers } from 'ethers';
import { Buffer } from 'buffer';
const fa = require("#glif/filecoin-address");
import { DAPP_CONTRACT } from "#env"
const networkInfo = {
defaultNetwork: "hyperspace",
networks: {
hyperspace: {
chainId: 3141,
url: "https://api.hyperspace.node.glif.io/rpc/v1",
},
},
}
const MintTokens = async (key, amount) => {
try {
let cleanKey = key.replace(/["]/g, "");
let provider = new ethers.providers.JsonRpcProvider(FevmProviderUrl, networkInfo.networks.hyperspace);
const wallet = new ethers.Wallet(cleanKey, provider);
const signer = wallet.connect(provider);
const f4ContractAddress = fa.newDelegatedEthAddress(DAPP_CONTRACT).toString();
const f4WalletAddress = fa.newDelegatedEthAddress(wallet.address).toString();
const f4ActorAddress = fa.newActorAddress(wallet.address).toString();
console.log("Ethereum Contract address in .env (this address should work for most tools):", DAPP_CONTRACT);
console.log("f4address Contract (also known as t4 address on testnets):", f4ContractAddress);
console.log("f4address Actor (also known as t4 address on testnets):", f4ActorAddress);
console.log("f4address Wallet (also known as t4 address on testnets):", f4WalletAddress);
console.log("Provider:", provider);
console.log("Eth Wallet:", wallet);
console.log("Minting FitMint");
const FitMints = new ethers.Contract(DAPP_CONTRACT, ContractABI.abi, signer)
console.log("Contract:", FitMints)
// //Call the mint method
let result = await FitMints.mint(wallet.address, amount, { gasLimit: 30000 })
if(result) {
console.log("Minting Token:", result)
return result
} else {
return {"result": result.toString()}
}
} catch (error) {
console.log(error)
return error
}
}
export default {
MintTokens,
};
Output of mint token:
[Error: processing response error (bod
c":"2.0","id":50,"error":{"code":1,
"failed to look up actor state nonce: resol
failed (t410fzzzo2dimmzy63d4vgxvfak6wvxdhcr
resolve address t410fzzzo2dimmzy63d4vgxvfak6
j3vy: actor not found: validation failure"}
{"code":1}, requestBody="{"method":"eth_s
ction","params":["0x02f8b1820c4580845968c6aa9ae8453d4e311f6749ae0c80b84440c10f1900008f9535ea502bd6adc671469c0000000000000000000000000001c001a00a9df20582d715909d4d7dd7606f2622e3f64c0faa03bdbc7b6b23cb5f26aaccbd12060c3djsonrpc":"2.0"}", requestMethod="POST", u.io/rpc/v1", code=SERVER_ERROR, version=web/
AI Said
Check the imports to make sure all necessary packages are imported correctly.
Verify the contract address in the .env file is correct.
Verify the network information is correctly set for the Hyperspace network.
Check the code for the getTokens and MintTokens functions to ensure all parameters are correctly passed.
Make sure the wallet address is correct and matches the f4 address.
Check the gas limit settings for the MintTokens function.
Ensure the request body and method are correctly set in the code.
Verify the URL for the Json RPC Provider is correct.
But Im just lost, thanks

ICE failed on firefox but peerJs's exemple work fine

I try to make a simple chat with peerJs to learn how to use peer to peer
when I try to connect two clients, this error happens on both client
WebRTC: ICE failed, your TURN server appears to be broken, see about:webrtc for more details
PeerJS: iceConnectionState is failed, closing connections
the peerJs demo work fine on Firefox (even when i run it locally)
and my code seem to work correctly on edge
I try in private with disabled add-on
my media.peerconnection.ice.proxy_only is set to false.
Any setting related to ice or peer is set to default
My code
both client have the same code, they try to connect to the client in the url
exemple:
i open http://localhost:5173/room who get an peerId and rename itselfhttp://localhost:5173/room?roomId=198ec396-1691-48cf-b6ea-16d2102c4917 then i copy paste this link to another tab
<script lang="ts">
import { page } from '$app/stores';
import { Peer } from 'peerjs';
import { onMount } from 'svelte';
let peer: Peer;
onMount(() => {
const url = $page.url.searchParams;
peer = new Peer({ debug: 3 });
peer.on('open', function (id) {
console.log('open| id :', id);
if (url.has('roomId')) {
let conn = peer.connect(url.get('roomId')!, {
reliable: true
});
conn.on('open', function () {
console.log('test 1');
conn.send('test send 1');
});
}
// change url to be easily copied
window.history.replaceState({}, '', 'room?roomId=' + id);
});
peer.on('connection', function (connexion) {
console.log('test 2');
connexion.send('test send 2');
});
});
</script>
firefox log
edge log (seem to connect but test's send() not logged)

Socket.io with Vue3

I have a Vue 3 app and an express server. The server does not serve any pages just acts as an API so no socket.io/socket.io.js file is sent to client.
I am trying to set up socket.io in one of my vue components but whatever I try does not work. Using vue-3-socket.io keeps giving 't.prototype is undefined' errors.
I have tried vue-socket.io-extended as well with no luck.
Any advice would be appreciated as to the reason and solution for the error above, I have tried various SO solutions without success, and the best way forward.
You can use socket.io-client. I have used socket.io-client of 4.4.1 version.
step: 1
Write class inside src/services/SocketioService.js which returns an instance of socketio.
import {io} from 'socket.io-client';
class SocketioService {
socket;
constructor() { }
setupSocketConnection() {
this.socket = io(URL, {
transports: ["websocket"]
})
return this.socket;
}
}
export default new SocketioService();
Step 2:
Import SocketioService in App.vue. You can instantiate in any lifecycle hook of vue. I have instantiated on mounted as below. After instantiation, I am listening to welcome and notifications events and used quasar notify.
<script>
import { ref } from "vue";
import SocketioService from "./services/socketio.service.js";
export default {
name: "LayoutDefault",
data() {
return {
socket: null,
};
},
components: {},
mounted() {
const socket = SocketioService.setupSocketConnection();
socket.on("welcome", (data) => {
const res = JSON.parse(data);
if (res?.data == "Connected") {
this.$q.notify({
type: "positive",
message: `Welcome`,
classes: "glossy",
});
}
});
socket.on("notifications", (data) => {
const res = JSON.parse(data);
let type = res?.variant == "error" ? "negative" : "positive";
this.$q.notify({
type: type,
message: res?.message,
position: "bottom-right",
});
});
},
};
</script>

Nestjs - file upload with fastify multipart

I am trying to upload multiple files with nestjs using the fastify adapter. I can do so following the tutorial in this link -article on upload
Now this does the job of file upload using fastify-multipart, but I couldnt make use of the request validations before uploading,
for example, here is my rule-file-models (which later I wanted to save to postgre)
import {IsUUID, Length, IsEnum, IsString, Matches, IsOptional} from "class-validator";
import { FileExtEnum } from "./enums/file-ext.enum";
import { Updatable } from "./updatable.model";
import {Expose, Type} from "class-transformer";
export class RuleFile {
#Expose()
#IsUUID("4", { always: true })
id: string;
#Expose()
#Length(2, 50, {
always: true,
each: true,
context: {
errorCode: "REQ-000",
message: `Filename shouldbe within 2 and can reach a max of 50 characters`,
},
})
fileNames: string[];
#Expose()
#IsEnum(FileExtEnum, { always: true, each: true })
fileExts: string[];
#IsOptional({each: true, message: 'File is corrupated'})
#Type(() => Buffer)
file: Buffer;
}
export class RuleFileDetail extends RuleFile implements Updatable {
#IsString()
#Matches(/[aA]{1}[\w]{6}/)
recUpdUser: string;
}
And I wanted to validate the multipart request and see if these are set properly.
I cannot make it to work with event subscription based approach. Here are a few things I tried - adding the interceptor, to check for the request
#Injectable()
export class FileUploadValidationInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
const req: FastifyRequest = context.switchToHttp().getRequest();
console.log('inside interceptor', req.body);
// content type cmes with multipart/form-data;boundary----. we dont need to valdidate the boundary
// TODO: handle split errors based on semicolon
const contentType = req.headers['content-type'].split(APP_CONSTANTS.CHAR.SEMI_COLON)[0];
console.log(APP_CONSTANTS.REGEX.MULTIPART_CONTENT_TYPE.test(contentType));
const isHeaderMultipart = contentType != null?
this.headerValidation(contentType): this.throwError(contentType);
**// CANNOT check fir req.file() inside this, as it throws undefined**
return next.handle();
}
headerValidation(contentType) {
return APP_CONSTANTS.REGEX.MULTIPART_CONTENT_TYPE.test(contentType) ? true : this.throwError(contentType);
}
throwError(contentType: string) {
throw AppConfigService.getCustomError('FID-HEADERS', `Request header does not contain multipart type:
Provided incorrect type - ${contentType}`);
}
}
I wasnt able to check req.file() in the above interceptor. It throws as undefined. I tried to follow the fastify-multipart
But I wasnt able to get the request data in a prehandler as provided in the documentation for fastify-multipart
fastify.post('/', async function (req, reply) {
// process a single file
// also, consider that if you allow to upload multiple files
// you must consume all files othwise the promise will never fulfill
const data = await req.file()
data.file // stream
data.fields // other parsed parts
data.fieldname
data.filename
data.encoding
data.mimetype
// to accumulate the file in memory! Be careful!
//
// await data.toBuffer() // Buffer
//
// or
await pump(data.file, fs.createWriteStream(data.filename))
I tried getting via by registering a prehandler hook of my own like this (executed as iife)
(async function bootstrap() {
const appConfig = AppConfigService.getAppCommonConfig();
const fastifyInstance = SERVERADAPTERINSTANCE.configureFastifyServer();
// #ts-ignore
const fastifyAdapter = new FastifyAdapter(fastifyInstance);
app = await NestFactory.create<NestFastifyApplication>(
AppModule,
fastifyAdapter
).catch((err) => {
console.log("err in creating adapter", err);
process.exit(1);
});
.....
app.useGlobalPipes(
new ValidationPipe({
errorHttpStatusCode: 500,
transform: true,
validationError: {
target: true,
value: true,
},
exceptionFactory: (errors: ValidationError[]) => {
// send it to the global exception filter\
AppConfigService.validationExceptionFactory(errors);
},
}),
);
app.register(require('fastify-multipart'), {
limits: {
fieldNameSize: 100, // Max field name size in bytes
fieldSize: 1000000, // Max field value size in bytes
fields: 10, // Max number of non-file fields
fileSize: 100000000000, // For multipart forms, the max file size
files: 3, // Max number of file fields
headerPairs: 2000, // Max number of header key=>value pairs
},
});
(app.getHttpAdapter().getInstance() as FastifyInstance).addHook('onRoute', (routeOptions) => {
console.log('all urls:', routeOptions.url);
if(routeOptions.url.includes('upload')) {
// The registration actually works, but I cant use the req.file() in the prehandler
console.log('###########################');
app.getHttpAdapter().getInstance().addHook('preHandler', FilePrehandlerService.fileHandler);
}
});
SERVERADAPTERINSTANCE.configureSecurity(app);
//Connect to database
await SERVERADAPTERINSTANCE.configureDbConn(app);
app.useStaticAssets({
root: join(__dirname, "..", "public"),
prefix: "/public/",
});
app.setViewEngine({
engine: {
handlebars: require("handlebars"),
},
templates: join(__dirname, "..", "views"),
});
await app.listen(appConfig.port, appConfig.host, () => {
console.log(`Server listening on port - ${appConfig.port}`);
});
})();
Here is the prehandler,
export class FilePrehandlerService {
constructor() {}
static fileHandler = async (req, reply) => {
console.log('coming inside prehandler');
console.log('req is a multipart req',await req.file);
const data = await req.file();
console.log('data received -filename:', data.filename);
console.log('data received- fieldname:', data.fieldname);
console.log('data received- fields:', data.fields);
return;
};
}
This pattern of registring and gettin the file using preHandler works in bare fastify application. I tried it
Bare fastify server:
export class FileController {
constructor() {}
async testHandler(req: FastifyRequest, reply: FastifyReply) {
reply.send('test reading dne');
}
async fileReadHandler(req, reply: FastifyReply) {
const data = await req.file();
console.log('field val:', data.fields);
console.log('field filename:', data.filename);
console.log('field fieldname:', data.fieldname);
reply.send('done');
}
}
export const FILE_CONTROLLER_INSTANCE = new FileController();
This is my route file
const testRoute: RouteOptions<Server, IncomingMessage, ServerResponse, RouteGenericInterface, unknown> = {
method: 'GET',
url: '/test',
handler: TESTCONTROLLER_INSTANCE.testMethodRouteHandler,
};
const fileRoute: RouteOptions = {
method: 'GET',
url: '/fileTest',
preHandler: fileInterceptor,
handler: FILE_CONTROLLER_INSTANCE.testHandler,
};
const fileUploadRoute: RouteOptions = {
method: 'POST',
url: '/fileUpload',
preHandler: fileInterceptor,
handler: FILE_CONTROLLER_INSTANCE.fileReadHandler,
};
const apiRoutes = [testRoute, fileRoute, fileUploadRoute];
export default apiRoutes;
Could someone let me know the right the way to get the fieldnames , validate them befr the service being called in Nestjs
Well, I have done something like this and It works great for me. Maybe it can work for you too.
// main.ts
import multipart from "fastify-multipart";
const app = await NestFactory.create<NestFastifyApplication>(
AppModule,
new FastifyAdapter(),
);
app.register(multipart);
// upload.guard.ts
import {
Injectable,
CanActivate,
ExecutionContext,
BadRequestException,
} from "#nestjs/common";
import { FastifyRequest } from "fastify";
#Injectable()
export class UploadGuard implements CanActivate {
public async canActivate(ctx: ExecutionContext): Promise<boolean> {
const req = ctx.switchToHttp().getRequest() as FastifyRequest;
const isMultipart = req.isMultipart();
if (!isMultipart)
throw new BadRequestException("multipart/form-data expected.");
const file = await req.file();
if (!file) throw new BadRequestException("file expected");
req.incomingFile = file;
return true;
}
}
// file.decorator.ts
import { createParamDecorator, ExecutionContext } from "#nestjs/common";
import { FastifyRequest } from "fastify";
export const File = createParamDecorator(
(_data: unknown, ctx: ExecutionContext) => {
const req = ctx.switchToHttp().getRequest() as FastifyRequest;
const file = req.incomingFile;
return file
},
);
// post controller
#Post("upload")
#UseGuards(UploadGuard)
uploadFile(#File() file: Storage.MultipartFile) {
console.log(file); // logs MultipartFile from "fastify-multipart"
return "File uploaded"
}
and finally my typing file
declare global {
namespace Storage {
interface MultipartFile {
toBuffer: () => Promise<Buffer>;
file: NodeJS.ReadableStream;
filepath: string;
fieldname: string;
filename: string;
encoding: string;
mimetype: string;
fields: import("fastify-multipart").MultipartFields;
}
}
}
declare module "fastify" {
interface FastifyRequest {
incomingFile: Storage.MultipartFile;
}
}
So I found a simpler alternative. I started using fastify-multer. I used it along with this awesome lib - which made me use the multer for fastify - #webundsoehne/nest-fastify-file-upload
These are the changes I made. I registered the multer content process.
app.register(multer( {dest:path.join(process.cwd()+'/upload'),
limits:{
fields: 5, //Number of non-file fields allowed
files: 1,
fileSize: 2097152,// 2 MB,
}}).contentParser);
Then in the controller - I use it as the nestjs doc says . This actually makes fasitfy work with multer
#UseInterceptors(FileUploadValidationInterceptor, FileInterceptor('file'))
#Post('/multerSample')
async multerUploadFiles(#UploadedFile() file, #Body() ruleFileCreate: RuleFileCreate) {
console.log('data sent', ruleFileCreate);
console.log(file);
// getting the original name of the file - no matter what
ruleFileCreate.originalName = file.originalname;
return await this.fileService.fileUpload(file.buffer, ruleFileCreate);
}
BONUS - storing the file in local and storing it in DB - Please refer
github link

Where do I add this websocket code in the new nuxt.js setup since it does not have server?

I am using the new version of Nuxt which does not come with a server folder
In the old version, you had a server folder and an index.js which contained the app
I want to add the websocket WS library with the new version of NuxtJS
The library requires an instance of server which you create by calling http.createServer(app) meaning an instance of the running express app. You would use this server instance now to listen to 3000 in the index.js file. Also create a sessionHandler which you obtain by calling session({}) with the express-session library
const WebSocket = require('ws')
function websocket({ server, sessionHandler, logger }) {
// https://github.com/websockets/ws/blob/master/examples/express-session-parse/index.js, if you pass a server instance here 'upgrade' handler will crash
const wss = new WebSocket.Server({ noServer: true })
function noop() {}
function heartbeat() {
this.isAlive = true
}
wss.on('connection', (ws, request, client) => {
ws.isAlive = true
ws.on('pong', heartbeat)
ws.on('message', (msg) => {
logger.info(`Received message ${msg} from user ${client}`)
ws.send(true)
})
})
server.on('upgrade', (request, socket, head) => {
sessionHandler(request, {}, () => {
logger.info(`${JSON.stringify(request.session)} WEBSOCKET SESSION PARSED`)
wss.handleUpgrade(request, socket, head, (ws) => {
wss.emit('connection', ws, request)
})
})
})
// TODO use a setTimeout here instead of a setInterval
setInterval(function ping() {
// wss.clients => Set
wss.clients.forEach(function each(ws) {
if (ws.isAlive === false) return ws.terminate()
ws.isAlive = false
ws.ping(noop)
})
}, 30000)
return wss
}
module.exports = websocket
Does anyone know how I can make this work on the new Nuxt setup without the server folder
You can create a custom module and use nuxt hooks to get a server instance on listen event.
Create modules/ws.js:
const WebSocket = require('ws')
const wss = new WebSocket.Server({ noServer: true })
wss.on('connection', ws => {
ws.on('message', message => {
console.log('received: %s', message);
})
ws.send('Hello')
})
export default function () {
this.nuxt.hook('listen', server => {
server.on('upgrade', (request, socket, head) => {
wss.handleUpgrade(request, socket, head, ws => {
wss.emit('connection', ws);
})
})
})
}
And register the module in nuxt.config.js:
export default {
modules: [
'~/modules/ws'
]
}
In your case you could create a module directory instead of a single file to store multiple related files.
Create modules/ws/index.js
const websocket = require('./websocket') // this is your file
export default function () {
this.nuxt.hook('listen', server => {
websocket({
server,
sessionHandler (request, _, cb) { // example
cb()
},
logger: console // example
})
})
}
Then copy your file to modules/ws/websocket.js. You can use module.exports which is CommonJS format or change it into ES Module format, Nuxt(Webpack) can handle that.
In your code I notice that ws.send(true) cause an error TypeError [ERR_INVALID_ARG_TYPE]: The first argument must be of type string or an instance of Buffer, ArrayBuffer, or Array or an Array-like Object. Received type boolean (true) which basically mean you cannot send boolean.