Chrome Storage Set - Asynchronous Issues onMessage [duplicate] - javascript

I'm developing a chrome extension and I will store objects sent by server.
For example, I will receive:
command = {id:"1", type: "A", size: "B", priority: "C"}
If I had a database, I would insert it as a line in table commands.
Using chrome.storage, I'm storing an array of these object in key commands.
But, when I receive a new command by server, I have to get from local storage, update the array and then set again. I'm worried about cases when I receive another command while I'm getting and setting or while I delete a stored command. I'm thinking about semaphores, but I don't know if it's a great idea.
Can someone suggest me what to do?
thanks!

Extensions can use a database: IndexedDB (the sample code may look convoluted, but it's pretty simple in the actual extensions, for example two small functions here, getStyles and saveStyle, or IDB-keyval wrapper library).
If you want to use chrome.storage, just maintain a global queue array that is populated by the server listener:
queue.push(newItem);
updateStorage();
and processed in chrome.storage.local.get callback:
function updateStorage() {
if (!queue.length || updateStorage.running) {
return;
}
updateStorage.running = true;
chrome.storage.local.get('commands', data => {
data.commands = [].concat(data.commands || [], queue);
queue = [];
chrome.storage.local.set(data, () => {
updateStorage.running = false;
if (queue.length) updateStorage();
});
});
}

Related

Is it possible to link a random html site with node javascript?

Is it possible to link a random site with node.js, when I say that, Is it possible to link it with only a URL, if not then I'm guessing it's having the file.html inside the javascript directory. I really wanna know if it's possible because the html is not mine and I can't add the line of code to link it with js that goes something like (not 100% sure) <src = file.html>
I tried doing document = require('./page.html'); and ('./page') but it didn't work and when I removed the .html at the end of require it would say module not found
My keypoint is that the site shows player count on some servers, and I wanna get that number by linking it with js and then using it in some code which I have the code to (tested in inspect element console) but I don't know how to link it properly to JS.
If you wanna take a look at the site here it is: https://portal.srbultras.info/#servers
If you have any ideas how to link a stranger's html with js, i'd really appreciate to hear it!
You cannot require HTML files unless you use something like Webpack with html-loader, but even in this case you can only require local files. What you can do, however, is to send an HTTP Request to the website. This way you get the same HTML your browser receives whenever you open a webpage. After that you will have to parse the HTML in order to get the data you need. The jsdom package can be used for both steps:
const { JSDOM } = require('jsdom');
JSDOM.fromURL('https://portal.srbultras.info/')
.then(({ window: { document }}) => {
const servers = Array.from(
document.querySelectorAll('#servers tbody>tr')
).map(({ children }) => {
const name = children[3].textContent;
const [ip, port] = children[4]
.firstElementChild
.textContent
.split(':');
const [playersnum, maxplayers] = children[5]
.lastChild
.textContent
.split('/')
.map(n => Number.parseInt(n));
return { name, ip, port, playersnum, maxplayers };
});
console.log(servers);
/* Your code here */
});
However, grabbing the server information from a random website is not really what you want to do, because there is a way to get it directly from the servers. Counter Strike 1.6 servers seem to use the GoldSrc / Source Server Protocol that lets us retrieve information about the servers. You can read more about the protocol here, but we are just going to use the source-server-query package to send queries:
const query = require('source-server-query');
const servers = [
{ ip: '51.195.60.135', port: 27015 },
{ ip: '51.195.60.135', port: 27017 },
{ ip: '185.119.89.86', port: 27021 },
{ ip: '178.32.137.193', port: 27500 },
{ ip: '51.195.60.135', port: 27018 },
{ ip: '51.195.60.135', port: 27016 }
];
const timeout = 5000;
Promise.all(servers.map(server => {
return query
.info(server.ip, server.port, timeout)
.then(info => Object.assign(server, info))
.catch(console.error);
})).then(() => {
query.destroy();
console.log(servers);
/* Your code here */
});
Update
servers is just a normal JavaScript array consisting of objects that describe servers, and you can see its structure when it is logged into the console after the information has been received, so it should not be hard to work with. For example, you can access the playersnum property of the third server in the list by writing servers[2].playersnum. Or you can loop through all the servers and do something with each of them by using functions like map and forEach, or just a normal for loop.
But note that in order to use the data you get from the servers, you have to put your code in the callback function passed to the then method of Promise.all(...), i.e. where console.log(servers) is located. This has to do with the fact that it takes some time to get the responses from the servers, and for that reason server queries are normally asynchronous, meaning that the script continues execution even though it has not received the responses yet. So if you try to access the information in the global scope instead of the callback function, it is not going to be there just yet. You should read about JavaScript Promises if you want to understand how this works.
Another thing you may want to do is to filter out the servers that did not respond to the query. This can happen if a server is offline, for example. In the solution I have provided, such servers are still in the servers array, but they only have the ip and port properties they had originally. You could use filter in order to get rid of them. Do you see how? Tell me if you still need help.

Js-IPFS Error: cid.toBaseEncodedString() is not a function

I'm currently working on the backend of a website that would work similar to YouTube but only use IPFS for storage, meaning if you want to "upload" videos to the site it would already have to be on the IPFS network. The actual function is more of an Index than anything else but it was something that I wanted to tackle.
The section I'm working on is intended to verify the integrity of the CID hashes by making sure that there are still providers on the network for that specific content. If there aren't any then the CID and any information associated will get removed from my database but I'm currently getting an issue when trying using the ipfs.dhs.findProvs function.
Here is part of my code:
const ipfs = await IPFS.create({
libp2p: { config: { dht: { enabled: true } } },
});
for (var i of integrityData) {
let cid = new CID(i.CID);
console.log(cid);
let providers = ipfs.dht.findProvs(cid, { numProviders: 2 });
for await (const provider of providers) {
console.log(provider);
}
}
Error Log:
C:\Users\...\node_modules\libp2p-kad-dht\src\providers.js:202
this._log('getProviders %s', cid.toBaseEncodedString())
^
TypeError: cid.toBaseEncodedString is not a function
To further explain my code, the for loops is iterating the JSON content received the Database after querying for all the CIDs in it. i.CID does return the correct CID as a string which I then create a CID object from and pass to the function here ipfs.dht.findProvs(cid, { numProviders: 2 });. The nested for loop is there to iterate through the object that is received but I haven't made it to that stage as I keep getting the same error.

Express with JSON Data Control

I use lowDB dependency to control the JSON Data with Express and actually it works. But there is a bug and I cannot find how to solve it.
I create /create page to add information in JSON file and it contains 4 form and submit button.
And In express I code like this. each forms data will save it in variable and push with lowdb module.
router.post('/post', function (req, res) {
let pjName = req.body.projectName;
let pjURL = req.body.projectURL;
let pjtExplanation = req.body.projectExplanation;
let pjImgURL = req.body.projectImgURL;
console.log(pjName);
db.get('project').push({
name: pjName,
url: pjURL,
explanation: pjtExplanation,
imgurl: pjImgURL
}).write();
console.log(db.get('project'));
console.log(db.get('project').value());
res.redirect('/');
})
And it works well. But when I modify the JSON file myself (ex. reset the JSON file) and execute again. It shows the data that I reset before. I think in this app somewhere saves the all data and show save it in array again.
And When I shutdown the app in CMD and execute again, the Array is initialized.
As you may already know the lowdb persist the data into your secondary memory (hdd), and may return a promise depending on your environment when you call write method.As mentioned in the doc
Persists database using adapter.write (depending on the adapter, may return a promise).
So the data may be still getting write when you read them, so the old data is queried. Try this,
db.get('project').push({
name: pjName,
url: pjURL,
explanation: pjtExplanation,
imgurl: pjImgURL
}).write().then(() => {
console.log(db.get('project'));
console.log(db.get('project').value());
});

Meteor: Best practice for modifying document data with user data

Thanks for looking at my question. It should be easy for anyone who has used Meteor in production, I am still at the learning stage.
So my meteor setup is I have a bunch of documents with ownedBy _id's reflecting which user owns each document (https://github.com/rgstephens/base/tree/extendDoc is the full github, note that it is the extendDoc branch and not the master branch).
I now want to modify my API such that I can display the real name of each owner of the document. On the server side I can access this with Meteor.users.findOne({ownedBy}) but on the client side I have discovered that I cannot do this due to Meteor security protocols (a user doesnt have access to another user's data).
So I have two options:
somehow modify the result of what I am publishing to include the user's real name on the server side
somehow push the full user data to the clientside and do the mapping of the _id to the real names on the clientside
what is the best practice here? I have tried both and here are my results so far:
I have failed here. This is very 'Node' thinking I know. I can access user data on clientside but Meteor insists that my publications must return cursors and not JSON objects. How do I transform JSON objects into cursors or otherwise circumvent this publish restriction? Google is strangely silent on this topic.
Meteor.publish('documents.listAll', function docPub() {
let documents = Documents.find({}).fetch();
documents = documents.map((x) => {
const userobject = Meteor.users.findOne({ _id: x.ownedBy });
const x2 = x;
if (userobject) {
x2.userobject = userobject.profile;
}
return x2;
});
return documents; //this causes error due to not being a cursor
}
I have succeeded here but I suspect at the cost of a massive security hole. I simply modified my publish to be an array of cursors, as below:
Meteor.publish('documents.listAll', function docPub() {
return [Documents.find({}),
Meteor.users.find({}),
];
});
I would really like to do 1 because I sense there is a big security hole in 2, but please advise on how I should do it? thanks very much.
yes, you are right to not want to publish full user objects to the client. but you can certainly publish a subset of the full user object, using the "fields" on the options, which is the 2nd argument of find(). on my project, i created a "public profile" area on each user; that makes it easy to know what things about a user we can publish to other users.
there are several ways to approach getting this data to the client. you've already found one: returning multiple cursors from a publish.
in the example below, i'm returning all the documents, and a subset of all the user object who own those documents. this example assumes that the user's name, and whatever other info you decide is "public," is in a field called publicInfo that's part of the Meteor.user object:
Meteor.publish('documents.listAll', function() {
let documentCursor = Documents.find({});
let ownerIds = documentCursor.map(function(d) {
return d.ownedBy;
});
let uniqueOwnerIds = _.uniq(ownerIds);
let profileCursor = Meteor.users.find(
{
_id: {$in: uniqueOwnerIds}
},
{
fields: {publicInfo: 1}
});
return [documentCursor, profileCursor];
});
In the MeteorChef slack channel, #distalx responded thusly:
Hi, you are using fetch and fetch return all matching documents as an Array.
I think if you just use find - w/o fetch it will do it.
Meteor.publish('documents.listAll', function docPub() {
let cursor = Documents.find({});
let DocsWithUserObject = cursor.filter((doc) => {
const userobject = Meteor.users.findOne({ _id: doc.ownedBy });
if (userobject) {
doc.userobject = userobject.profile;
return doc
}
});
return DocsWithUserObject;
}
I am going to try this.

Querying By Multiple Keys in Firebase

I have a list of known keys in my Firebase database
-Ke1uhoT3gpHR_VsehIv
-Ke8qAECkZC9ygGW3dEJ
-Ke8qMU7OEfUnuXSlhhl
Rather than looping through each of these keys to fetch a snapshot of their respective object, how can I query for each of these keys in one single, unified request? Does Firebase provide this?
I've discovered the Promise.all() function which looks promising (no pun intended I swear) but I'm not sure how to implement it using the standard way of fetching firebase data like so
var userId = firebase.auth().currentUser.uid;
return firebase.database().ref('/users/' + userId).once('value').then(function(snapshot) {
var username = snapshot.val().username;
});
Thanks for any help!
As David's comment suggested: if the items are in some way related, you may be able to built a query to get them all.
Otherwise this would do the trick:
var keys = [
"-Ke1uhoT3gpHR_VsehIv",
"-Ke8qAECkZC9ygGW3dEJ",
"-Ke8qMU7OEfUnuXSlhhl"
];
var promises = keys.map(function(key) {
return firebase.database().ref("/items/").child(key).once("value");
});
Promise.all(promises).then(function(snapshots) {
snapshots.forEach(function(snapshot) {
console.log(snapshot.key+": "+snapshot.val());
});
});
Note that retrieving each item with a separate request is not as slow as you may think, since the requests are all sent over a single connection. For a longer explanation of that, see Speed up fetching posts for my social network app by using query instead of observing a single event repeatedly.

Categories