Getting data from / writing data to localhost using Express

I am trying to create a webapp for a web art class using node (w / npm) and express. The idea is for the body of the site to be just one color, but anyone can send hexcode / CSS text to a Twilio number and the site color will instantly change to that color value.

Essentially, the way it works is that the server receives a POST request from Twilio to http://example.com/message that contains the body of the text message. It writes it to a temporary file at ~ / app / .data / color.tmp, which is accessed by the client with a jQuery.get () call for http://example.com/color , which returns

So here's the problem: I have a version of the application running on glitch.me, so I know this code might work, but I have a lot of problems getting it to work on my domain. I installed the app and can run it with npm and it successfully shows me an HTML page, but Chrome devtools shows the script gets 403 when it tries to access / color. Also, new texts on my site don't change the color value in /.data/color.tmp. I thought it might be a permissions issue, but I checked them and they look ok.

Here's the server file and script in the index.html page:

app /server.js

var express = require('express');
var bodyParser = require('body-parser');
var fs = require('fs');
var app = express();
app.use(bodyParser.urlencoded({extended: false})); 
var dataPath = '.data/color.tmp';


// set a new color (saves posted color to disk)
app.post("/message", function (request, response) {
  var dataStr = JSON.stringify(request.body.Body);
  fs.writeFile(dataPath, dataStr);
  response.end();
});

// get the saved color (reading from disk)
app.get("/color", function (request, response) {
  var dataStr = fs.readFileSync(dataPath).toString();
  response.send(JSON.parse(dataStr));
});

app.get("/", function (request, response) {
  response.sendFile(__dirname + '/views/index.html');
});

var listener = app.listen(process.env.PORT, function () {
  console.log('listening on port ' + listener.address().port);
});

      

app / views / index.html

    <script>
      // checks server for color value and sets background
      function checkForColorChange() {
        $.get('/color', function getColorComplete(data) {
          document.body.style.backgroundColor = data;
            console.log(data);
        })
      }

      // Poll the server at 2000ms interval
      setInterval(checkForColorChange, 2000);

      checkForColorChange();
    </script>

      

Anyway, I feel like I'm missing something really obvious if it works so easily on Glitch and won't be on my website, but I've been stuck for days and not making any progress! Any help would be so appreciated. Let me know if anything is unclear.

+3


source to share


2 answers


(see update below for a working example)

TL; DR is an example:
webicon-github.svg Deploy to Heroku

Original Answer

There are several problems in your code:

  • you are not checking for errors
  • you are using blocking functions
  • you are implicitly relying on file permissions, but you are not checking it.
  • you are using string concatenation instead of path.join

    path concatenation
  • you are constantly polling for new data, not expecting it to change.
  • you haven't caught function exceptions that might throw an exception
  • you are not waiting for async operations to complete and you will not handle errors.

The main problem you are currently facing is most likely with file permissions. The good news is that you don't need file access for what you do, and using files is not optimal for that. All you need is to store the color in a variable, if you don’t need it, it persists between server restarts - and even if you do, I would use a simple database for that.

For example:

// some initial value:
var color = '#ffffff';

app.post("/message", function (request, response) {
  var color = request.body.Body;
  response.end();
});

// get the saved color (reading from disk)
app.get("/color", function (request, response) {
  response.send(color);
});

app.get("/", function (request, response) {
  response.sendFile(__dirname + '/views/index.html');
});

var listener = app.listen(process.env.PORT, function () {
  console.log('listening on port ' + listener.address().port);
});

      

This is the first change I would use - don't rely on filesystem, permissions, race conditions, etc.

Another issue you ran into with your code was using blocking functions inside request handlers. You should never use the blocking function (those with "Sync" in their name) other than the first tick of the event loop.

Another improvement I would make would be to use WebSocket or Socket.io instead of polling for data at regular intervals. This would be very easy to code. See this answer for examples:

On the plus side, all of your students will get the color instantly, and at the same time, not at random times spanning 2 seconds.

Update

I wrote an example of what I described above.

The POST endpoint is slightly different - it uses the /color

route and color=#abcdef

instead of /message

and Body=...

, but you can easily change it if you like - below.



Server code - server.js:

// requires removed for brevity

const app = express();
const server = http.Server(app);
const io = socket(server);

let color = '#ffffff';

app.use(bodyParser.urlencoded({ extended: false }));
app.use('/', express.static(path.join(__dirname, 'html')));

io.on('connection', (s) => {
  console.log('Socket.io client connected');
  s.emit('color', color);
});

app.post('/color', (req, res) => {
  color = req.body.color;
  console.log('Changing color to', color);
  io.emit('color', color);
  res.send({ color });
});

server.listen(3338, () => console.log('Listening on 3338'));

      

HTML page - index.html:

<!doctype html>
<html lang=en>
<head>
<meta charset=utf-8>
<meta name=viewport content="width=device-width, initial-scale=1">
<title>Node Live Color</title>
<link href="/style.css" rel=stylesheet>
</head>
<body>
<h1>Node Live Color</h1>
<script src="/socket.io/socket.io.js"></script>
<script src="/script.js"></script>
</body>
</html>

      

Stylesheet - style.css:

body {
  transition: background-color 2s ease;
  background-color: #fff;
}

      

Client side JavaScript - script.js:

var s = io();
s.on('color', function (color) {
  document.body.style.backgroundColor = color;
});

      

What's especially interesting is the simple client-side code.

For your original endpoint, use this in server.js:

app.post('/message', (req, res) => {
  color = req.body.Body;
  console.log('Changing color to', color);
  io.emit('color', color);
  res.end();
});

      

A complete example is available on GitHub:

I've tested it locally and on Heroku. You can click this button to deploy it to Heroku and test yourself:

Deploy to Heroku

Enjoy.

0


source


I think the problem is var dataStr = fs.readFileSync(dataPath).toString();

. Change dataPath

as follows:

 var dataPath = __dirname + '/data/color.tmp';

      



And also make sure the file has read / write permission.

0


source







All Articles