How to use a same DB with 2 different React applications - javascript

I have to make an offline application that sync with another online app when it can.
I developed the offline app using PouchDB. I create this app thanks to the github repo create-react-app. This app is running at localhost:3000. In this app, I create and manage a little DB named "patientDB".
I manage the db with the classical put method like we can see in the documentation:
var db = new PouchDB('patientDB')
db.put({
_id: 'dave#gmail.com',
name: 'David',
age: 69
});
With the development tool for chrome given by PouchDB, I can see that the DB is working like I want (the documents are created):
The other application is another React application with a node server. During the development, this app is running at localhost:8080.
In this app I try to fetch all the docs contained in the "patientDB" with the following code:
const db = new PouchDB('patientDB', { skip_setup: true });
db.info()
.then(() => {
console.log("DBFOUND")
db.allDocs({include_docs: true})
.then(function (result) {
console.log("RESULT" , result)
}).catch(function (err) {
console.log("NOPE")
console.log(err);
});
})
My problem is that I can't get the "patientDB" created with the offline app in the online app. When I do a var db = new PouchDB ('patientDB') it create a new and empty db because it can't find a db which is already present.
I use google chrome to run all my application so I thought that the dbs could be shared.
However I did little and very simple tests with two html files:
First.html which initialize a new db with a doc
Second.html which read the db create in First.html
In this case, I can fetch the doc created with First.html in Second.hmtl even if the are two separated "website".
I think that the application which run at a localhost are like isolated of the rest of the application even if, like I said before, I use the same browser for all my applications...
I don't know what to do or I don't know if it's even possible to do what I want to do. If someone has an idea for me, I would be pleased.
EDIT
I can see why my DBs are not shared:
When I look at all my local DBs after running an html file I can see the following thing :
As we can see, the DBs come from the files _pouch_DB_NAME - file://
When I check my DB from the application running localy (localhost), I can see this :
The DB don't come from file but from localhost:8080
If you know how I can fetch doc from a local db in an app running in a server, it could be really helpful for me!

PouchDB is using IndexedDB in the browser, which adheres to a same-origin policy. MDN says this:
IndexedDB adheres to a same-origin policy. An origin is the domain, application layer protocol, and port of a URL of the document where the script is being executed. Each origin has its own associated set of databases. Every database has a name that identifies it within an origin.
So you have to replicate your local database to a central server in order to share the data. This could be a PouchDB Server together with your node app. You can also access PouchDB Server directly from the browser:
var db = new PouchDB('http://localhost:5984/patientDB')
As an alternative, you can use CouchDB or IBM Cloudant (which is basically hosted CouchDB).

Related

LocalStorage not showing with build file

I have implemented a small localStorage with react, where I save URI endpoints once the users enters them, and I call them on my componentDidMount function if they exist.
The setup seemed super simple and it totally worked while I was doing npm start on my dev files, however on building my project and hosting it locally using 'serve', I am not able to see my localStorage anymore. Does this have to do something with the build files or the way I'm serving them?
componentDidMount() {
userUri = localStorage.getItem('userUri');
tracesUri = localStorage.getItem('tracesUri');
if (userUri && tracesUri) {
this.setState({
userUri: userUri,
tracesUri: tracesUri
});
}
};
closeModal = () => {
this.setState({
showSettings: false
});
localStorage.setItem('userUri', this.state.userUri);
localStorage.setItem('tracesUri', this.state.tracesUri);
};
If I understand your question correctly, when you run your app locally, you are not able to see the data that was persisted when you ran your app via npm?
Something to keep in mind is that data stored via localStorage is restricted to the current document.origin see MDN docs here.
You need to ensure that you are testing/running locally at the same origin for the same persisted data to be visible in both cases.
You can add this code to your app:
console.log('Origin is:', document.origin);
This will print the origin to console, and then cross check this origin by running the app both via 'npm' and by hosting locally to verify that the origin is the same or different
Access to data stored in the browser such as localStorage and IndexedDB are separated by origin. Each origin gets its own separate storage, and JavaScript in one origin cannot read from or write to the storage belonging to another origin.[ref: link]
So I guess while you serve, you must be using a different port due to which you were not able to access the previous localStorage values.

Communicating with a web widget-Meteor, React, Node

I'm building a chat dashboard and widget with which a customer should be able to put the widget into their page. Some similar examples would be Intercom or Drift.
Currently, the "main" application is written in Meteor.js (it's front end is in React). I've written a <Widget /> component and thrown it inside a /widget directory. Inside this directory, I also have an index.jsx file, which simply contains the following:
import React from 'react';
import ......
ReactDOM.render(
<Widget/>,
document.getElementById('widget-target')
);
I then setup a webpack configuration with an entry point at index.jsx and when webpack is run spits out a bundle.js in a public directory.
This can then be included on another page by simply including a script and div:
<script src="http://localhost:3000/bundle.js" type="text/javascript"></script>
<div id="widget-target"></div>
A few questions:
What is wrong with this implementation? Are their any security issues to be aware of? Both the examples linked earlier seem make use of an iframe in one form or another.
What is the best way to communicate with my main meteor application? A REST API? Emit events with Socket.io? The widget is a chat widget, so I need to send messages back and forth.
How can I implement some sort of unique identifier/user auth for the user and the widget? Currently, the widget is precompiled.
1 What is wrong with this implementation? Are their any security issues to be aware of? Both the examples linked earlier seem make use of an iframe in one form or another.
As #JeremyK mentioned, you're safer within an iFrame. That being said, there's a middle route that many third parties (Facebook, GA, ...) are using, including Intercom:
ask users to integrate your bundled code within their webpage. It's then up to you to ensure you're not introducing a security vulnerability on their site. This code will do two things:
take care of setting up an iframe, where the main part of your service is going to happen. You can position it, style it etc. This ensure that all the logic happening in the iframe is safe and you're not exposed.
expose some API between your customer webpage and your iframe, using window messaging.
the main code (the iframe code) is then loaded by this first script asynchronously, and not included in it.
For instance Intercom ask customers to include some script on their page: https://developers.intercom.com/docs/single-page-app#section-step-1-include-intercom-js-library that's pretty small (https://js.intercomcdn.com/shim.d97a38b5.js). This loads extra code that sets the iFrame and expose their API that will make it easy to interact with the iFrame, like closing it, setting user properties etc.
2 What is the best way to communicate with my main meteor application? A REST API? Emit events with Socket.io? The widget is a chat widget, so I need to send messages back and forth.
You've three options:
Build your widget as an entire Meteor app. This will increase the size of the code that needs to be loaded. In exchange for the extra code, you can communicate with your backend through the Meteor API, like Meteor.call, get the reactivity of all data (for instance if you send a response to a user through your main Meteor application, the response would pop up on the client with no work to do as long as they are on the same database (no need to be on the same server)), and the optimistic UI. In short you've all what Meteor offers here, and it's probably going to be easier to integrate with your existing backend that I assume is Meteor.
Don't include Meteor. Since you're building a chat app, you'll probably need socket.io over a traditional REST API. For sure you can do a mix of both
Use Meteor DDP. (it's kind of like socket.io, but for Meteor. Meteor app use that for all requests to the server) This will include less things that the full Meteor and probably be easier to integrate to your Meteor backend than a REST API / socket.io, and will be some extra work over the full Meteor.
3 How can I implement some sort of unique identifier/user auth for the user and the widget?
This part should probably do some work on the customer website (vs in your iframe) so that you can set cookies on his page, and send that data to your iframe that's gonna talk to your server and identify the user. Wether you use artwells:accounts-guest (that's based on meteor:accounts-base) is going to depend on wether you decide to include Meteor in your iframe.
If you don't have Meteor in your iframe, you can do something like:
handle user creation yourself, by simply doing on your server
.
const token = createToken();
Users.insert({ tokens: [token] });
// send the token back to your iframe
// and set is as a cookie on your customer website
then for each call to your server, on your iframe:
.
let token;
const makeRequest = async (request) => {
token = token || getCookieFromCustomerWebsite();
// pass the token to your HTTP / socket.io / ... request.
// in the header of whatever
return await callServer(token, request);
};
in the server have a middleware that sets the user. Mine looks like:
.
const loginAs = (userId, cb) => {
DDP._CurrentInvocation.withValue(new DDPCommon.MethodInvocation({
isSimulation: false,
userId,
}), cb);
};
// my middleware that run on all API requests for a non Meteor client
export const identifyUserIfPossible = (req, res, next) => {
const token = req.headers.authorization;
if (!token) {
return next();
}
const user = Users.findOne({ tokens: token });
if (!user) {
return next();
}
loginAs(user._id, () => {
next();
// Now Meteor.userId() === user._id from all calls made on that request
// So you can do Meteor.call('someMethod') as you'd do on a full Meteor stack
});
};
Asking your customers to embed your code like this doesn't follow the principles of Security by Design.
From their point of view, you are asking them to embed your prebundled code into their website, exposing their site up to any hidden security risks (inadvertent or deliberately malicious) that exist in your code which would have unrestricted access to their website's DOM, localstorage, etc.
This is why using an iframe is the prefered method to embed third party content in a website, as that content is sandboxed from the rest of it's host site.
Further, following the security principle of 'Least Privilege' they (with your guidance/examples) can set the sandbox attribute on the iframe, and explicitly lockdown via a whitelist the privileges the widget will have.
Loading your widget in an iframe will also give you more flexibility in how it communicates with your servers. This could now be a normal meteor client, using meteor's ddp to communicate with your servers. Your other suggestions are also possible.
User auth/identification depends on the details of your system. This could range from using Meteor Accounts which would give you either password or social auth solutions. Or you could try an anonymous accounts solution such as artwells:accounts-guest.
html5rocks article on sandboxed-iframes

What is the right way to manage connections to mongoDB, using node?

I'm using node.js and mongoDB. Right now, for my test app, the connection to the db is in the main node file, but I guess this is a wrong practice.
What I want/need: a secure way (i.e. not storing password on files users can access) to connect to the db just when needed.
For example: I want several admin pages (users, groups, etc..). Each page should connect to the db, find some data, and display it. It also have a form for adding a document to the db and a delete option.
I thought maybe to create some kind of a connection function - send it what you want to do (add, update, find, delete), to where (collection name) and whatever it needs. But I can't just include this function, because then it'll reveal the password to the db. So what can I do?
Thanks!
I'm going to answer your question bit by bit.
Right now, for my test app, the connection to the db is in the main node file
This is fine, though you might want to put it in a separate file for easier reuse. NodeJS is a continuesly running process, so in theory you could serve all of your HTTP responses using the same connection to the database. In practice you'd want to create a connection pool, but the Mongodb driver for NodeJS already does this automatically.
Each page should connect to the db, find some data, and display it.
When you issue a query on the MongoDB driver, it will automatically use a connection from its internal connection pool, as long as you gave it the credentials when your application was starting up.
What I want/need: a secure way (i.e. not storing password on files users can access) to connect to the db just when needed.
I would advice to keep your application configuration (any variables that depend on the environment in which the app is running) in a separate file which you don't commit to your VCS. A module like node-config can help a great deal with that.
The code you will end up with, using node-config, is something like:
config/default.json:
{
"mongo": null
}
This is the default configuration file which you commit.
config/local.json:
{
"mongo": "mongo://user:pass#host:port/db"
}
The local.json should be ignored by your VCS. It contains secret sauce.
connection.js:
var config = require('config');
var MongoClient = require('mongodb').MongoClient;
var cache;
module.exports = function(callback){
if(cache){
return callback(cache);
}
MongoClient.connect(config.get('mongo'), function(err, db){
if(err){
console.error(err.stack);
process.exit(1);
}
cache = db;
callback(db);
});
}
An incomplete example of how you might handle reusing the database connection. Note how the configuration is gotten using config.get(*). An actual implementation should have more robust error handling and prevent multiple connections from being made. Using Promises would make all that a lot easier.
index.js:
var connect = require('./connection');
connect(function(db){
db.find({whatever: true})
});
Now you can just require your database file anywhere you want, and reuse the same database connection, which handles pooling for you and you don't have your passwords hard-coded anywhere.

Server-side Javascript in production fails to open connection to a named instance of SQL2008

I've got a production site that has been working for years with a SQL Server 2000 default instance on server named MDWDATA. TCP port 1433 and Named Pipes are enabled there. My goal is to get this web app working with a copy of the database upgraded to SQL Server 2008. I've installed SQL2008 with SP1 on a server called DEVMOJITO and tested the new database using various VB6 desktop programs that exercise various stored procs in a client-server fashion and parts of the website itself work fine against the upgraded database residing on this named instance of SQL2008. So, while I am happy that the database upgrade seems fine there is a part of this website that fails with this Named Pipes Provider: Could not open a connection to SQL Server [1231]. I think this error is misleading. I disabled Named Pipes on the SQL2000 instance used by the production site, restarted SQL and all the ASP code still continued to work fine (plus we have a firewall between both database servers and these web virtual directories on a public facing webserver.
URL to my production virtual directory which demos the working page:
URL to my development v-directory which demos the failing page:
All the code is the same on both prod and dev sites except that on dev I'm trying to connect to the upgraded database.
I know there are dozens of things to check which I've been searching for but here are a few things I can offer to help you help me:
The code that is failing is server-side Javascript adapted from Brent Ashley's "Javascript Remote Scripting (JSRS)" code package years ago. It operates in an AJAX-like manner by posting requests back to different ASP pages and then handling a callback. I think the key thing to point out here is how I changed the connection to the database: (I cannot get Javascript to format right here!)
function setDBConnect(datasource)
{
var strConnect; //ADO connection string
//strConnect = "DRIVER=SQL Server;SERVER=MDWDATA;UID=uname;PASSWORD=x; DATABASE=StagingMDS;";
strConnect = "Provider=SQLNCLI10;Server=DEVMOJITO\MSSQLSERVER2008;Uid=uname;Pwd=x;DATABASE=StagingMDS;";
return strConnect;
}
function serializeSql( sql , datasource)
{
var conn = new ActiveXObject("ADODB.Connection");
var ConnectString = setDBConnect(datasource);
conn.Open( ConnectString );
var rs = conn.Execute( sql );
Please note how the connection string differs. I think that could be the problem but I don't know what to do. I am surprised the error returned says "named pipes" was involved because I really wanted to use TCP. The connection string syntax here is the same as used successfully on a different part of the site which uses VBScript which I'll paste here to show:
if DataBaseConnectionsAreNeeded(strScriptName) then
dim strWebDB
Set objConn = Server.CreateObject("ADODB.Connection")
if IsProductionWeb() Then
strWebDB = "DATABASE=MDS;SERVER=MDWDATA;DRIVER=SQL Server;UID=uname;PASSWORD=x;"
end if
if IsDevelopmentWeb() Then
strWebDB = "Provider=SQLNCLI10;Server=DEVMOJITO\MSSQLSERVER2008;Database=StagingMDS;UID=uname;PASSWORD=x;"
end if
objConn.ConnectionString = strWebDB
objConn.ConnectionTimeout = 30
objConn.Open
set oCmd = Server.CreateObject("ADODB.Command")
oCmd.ActiveConnection = objConn
This code works in both prod and dev virtual directories and other code in other parts of the web which use ASP.NET work against both databases correctly. Named pipes and TCP are both enabled on each server. I don't understand the string used by the Pipes but I am using the defaults always.
I wonder why the Javascript call above results in use of named pipes instead of TCP. Any ideas would be greatly appreciated.
Summary of what I did to get this working:
Add an extra slash to the connection string since this is server-side Javascript:
Server=tcp:DEVMOJITO\MSSQLSERVER2008,1219;
Explicitly code tcp: as a protocol prefix and port 1219. I learned that by default a named instance of SQL uses dynamic porting. I ended up turning that off and chose, somewhat arbitrarily, the port 1219, which dynamic had chosen before I turned it off. There are probably other ways to get this part working.
Finally, I discovered that SET NOCOUNT ON needed to be added to the stored procedure being called. Otherwise, the symptom is the message: "Operation is not allowed when the object is closed".

Websocket connection - no valid credentials

I'm trying to create a simple nodejs application that connects to the pathofexile.com/trade api.
The problem with this API is that is that you cannot use it unless you're logged in on the main website (my code works in the browser, but I'm trying to make it into a desktop application). There are several other applications that solves this issue by creating a session ID cookie with a users session ID (ID that you can get by logging in to the website). Unfortunately the documentation of the API is very limited and I havn't been able to find any information on how I can create/use the cookie as needed.
If I try to connect to the websocket, without being logged in to the main pathofexile website, I get the following error:
VM58:1 WebSocket connection to 'wss://www.pathofexile.com/api/trade/live/Metamorph/e602K4cL' failed: HTTP Authentication failed; no valid credentials available
I've tried using my sessionID to create a cookie like this by using the built in features in node:
const cookie = { name: 'POESESSID', value: '3acbf42fb842aasdqwe1a0c355f',domain:
'.pathofexile.com' }
session.defaultSession.cookies.set(cookie)
.then(() => {
// success
console.log("Cookie set (?)")
}, (error) => {
console.error(error)
})
Unfortunately, this does not work. I'm very unfamiliar with websockets (only started playing around with any of this a few days ago by accident), and I'm even less familiar with how websockets access and get data from cookies.
I've tried other modules like npm cookie-parser, npm request and npm needle to no avail.
The closest I've gotten to an answer is from a one year old reddit post where the user used C# to get this to work.
This is the code used in that example:
// Setup HTTP connection
HttpClientHandler handler = new HttpClientHandler();
CookieContainer cookieContainer = new CookieContainer();
cookieContainer.Add(composeUrl, new Cookie("POESESSID", sessionId));
handler.CookieContainer = cookieContainer;
HttpClient client = new HttpClient(handler);
If someone could help shine some light on this I'd be very appreciative. I understand that this question is incredibly niche and perhaps I'm asking it in the wrong forum, but I really don't know where to turn.
Appreciate any help!
//Alex

Categories

Resources