簡単なPost用serverを作る

Twilioのcallback statusを保存するために必要になったのでサクッと作る

Twilio
[status-callbacks](https://www.twilio.com/docs/api/video/status-callbacks)

Node/Expressを使用するので、"ベスト・プラクティス: セキュリティー"を考慮する

Express
[security](http://expressjs.com/ja/advanced/best-practice-security.html)


##1.構成

Node    : v0.10.48
Express : 4.15.4
OS      : CentOS release 6.8
Port　　 : 8443

##2. 準備

HTTPS化するので事前に証明書を用意
今回は"letsencrypt"を使用

[letsencrypt](https://letsencrypt.jp/usage/install-certbot.html#CentOS6)

logディレクトリを作成

```
#mkdir -p /var/log/access
```

Postされたデータを保存するためredisを用意

```
#rpm -Uvh http://download.fedoraproject.org/pub/epel/6/x86_64/epel-release-6-8.noarch.rpm
#rpm -Uvh http://rpms.famillecollet.com/enterprise/remi-release-6.rpm
#yum --enablerepo=remi,remi-test install redis
#/etc/init.d/redis start
```

iptablesを許可

```
-A INPUT -m state --state NEW -m tcp -p tcp --dport 8443 -j ACCEPT
```

Node/Express/foreverなどpackegeをinstallする


##2. Node script

```app.js

// set modules
const basicAuth = require('basic-auth-connect');
const bodyParser = require('body-parser');
const express = require('express');
const fs = require('fs');
const helmet = require('helmet');
const https = require('https');
const morgan = require('morgan');
const client = require('redis').createClient();

// init express
const app = express();

// set cert and key
var options = {
  key: fs.readFileSync( '/etc/letsencrypt/hoge/privkey.pem' ),
  cert: fs.readFileSync( '/etc/letsencrypt/hoge/fullchain.pem' )
};

// post param parser
app.use(bodyParser.urlencoded({
    extended: true
}));
app.use(bodyParser.json());

// set basic auth
app.use(basicAuth('user_name', 'password'));

// set logging
var stream = fs.createWriteStream('/var/log/access/access.log', { flags: 'a' });
app.use(morgan({ stream: stream }));

// check header
app.use(helmet());

// set route
app.post('/', function(req, res) {
    // save redis
    var key = req.body.To;
    var value = req.body.CallStatus;
    client.set(key, value, function(){
    });
    // return mesg
    res.send('POST request to the example.api.jp');
})

app.get('/CallStatus/:num?', function(req, res) {
    // restrict source ip
    var ip = req.connection.remoteAddress || req.socket.remoteAddress;
    var key = req.params.num
    if (ip.match(/xx\.xx\.xx\.xx/)){;
      client.get(key, function(err, val){
        if (err) return console.log(err);
        res.send(val);
      });
    }else{
      res.send('403 Forbidden', 403)};
});

// define server
var server = https.createServer(options,app);
server.listen(8443);

// console debug
console.log('Server start!');

```

##3.起動

```
forever start app.js
```


##3.Twilio設定

```call.py
call = client.calls.create(
to = "+81XXXXXXXXX",
from_ = "+81XXXXXXXXX",
timeout = "15",
url = "https://example.api.ppsys.jp/message",
status_callback = "https://user_name:password@example.api.ppsys.jp:8443",
status_callback_method = "POST",
tatus_callback_event = ["completed"]
)
```


##5.確認 

今回は    

callback statusの"To"を"key", "callstatus"を"value"とする

```
var key = req.body.To;  // call number
var value = req.body.CallStatus; // completed|no-answer など
```

PostはソースIP制限なし、GetはソースIP制限あり
Basic認証はGet/Post両方で行う

Get

```
# curl https://user_name:password@example.api.jp:8443/CallStatus/+81xxxxxxx
```

架電後、値を取得できれば問題なし