From 3ae54ef5e1443ded92f0a5b93bd542873ec8656f Mon Sep 17 00:00:00 2001 From: gumi Date: Thu, 29 Mar 2018 14:27:57 -0400 Subject: version v0.1.0 --- .gitignore | 2 + LICENSE | 15 +++++++ README.md | 1 + config.json.template | 15 +++++++ package.json | 16 +++++++ server.js | 117 +++++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 166 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 README.md create mode 100644 config.json.template create mode 100644 package.json create mode 100644 server.js diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f8a403c --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/config.json +/node_modules diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..fccea72 --- /dev/null +++ b/LICENSE @@ -0,0 +1,15 @@ +ISC License + +Copyright (c) 2018, The Mana World + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..05c11e5 --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +a RESTful API for The Mana World diff --git a/config.json.template b/config.json.template new file mode 100644 index 0000000..31968fb --- /dev/null +++ b/config.json.template @@ -0,0 +1,15 @@ +{ + "port": 8080, + + "sql": { + "host": "localhost", + "user": "db user", + "password": "db password", + "database": "db", + "table": "table" + }, + + "recaptcha": { + "secret": "recaptcha secret key" + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..006dca5 --- /dev/null +++ b/package.json @@ -0,0 +1,16 @@ +{ + "name": "tmw-api", + "version": "0.1.0", + "description": "TMW RESTful API", + "main": "server.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "The Mana World", + "license": "ISC", + "dependencies": { + "body-parser": "^1.18.2", + "express": "^4.16.3", + "mysql": "^2.15.0" + } +} diff --git a/server.js b/server.js new file mode 100644 index 0000000..ac45cc5 --- /dev/null +++ b/server.js @@ -0,0 +1,117 @@ +const express = require("express"); +const mysql = require("mysql"); +const bodyParser = require("body-parser"); +const https = require("https"); +const config = require("./config.json"); +const api = express(); + +const db = mysql.createConnection({ + host : config.sql.host, + user : config.sql.user, + password : config.sql.password, + database : config.sql.database +}); + +const checkCaptcha = (req, res, next) => { + const token = String(req.get("X-CAPTCHA-TOKEN")); + + if (!token.match(/^[a-zA-Z0-9-_]{8,}$/)) { + res.status(403).json({ + status: "error", + error: "no token sent" + }); + return; + } + + https.get(`https://www.google.com/recaptcha/api/siteverify?secret=${config.recaptcha.secret}&response=${token}`, (re) => { + re.setEncoding("utf8"); + re.on("data", response => { + const data = JSON.parse(response); + if (!data.success) { + console.error(`recaptcha returned an error: ${JSON.stringify(data)}`); + res.status(403).json({ + status: "error", + error: "captcha validation failed" + }); + return; + } + + next(); // challenge passed, so process the request + }); + }).on("error", error => { + console.error(error); + res.status(403).json({ + status: "error", + error: "recaptcha couldn't be reached" + }); + return; + }) +}; + + + +api.use(checkCaptcha); +api.use(bodyParser.json()); +api.post("/api/account", (req, res) => { + if (!req.body || !Reflect.has(req.body, "username") || + !Reflect.has(req.body, "password") || !Reflect.has(req.body, "email") || + !req.body.username.match(/^[a-zA-Z0-9]{4,23}$/) || + !req.body.password.match(/^[a-zA-Z0-9]{4,23}$/) || + !req.body.email.match(/^|(?:[a-zA-Z0-9.$&+=_~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*)$/) || + req.body.email.length >= 40) + { + res.status(400).json({ + status: "error", + error: "malformed request" + }); + return; + } + + let account = { + username: req.body.username, + password: req.body.password, + email: req.body.email || "a@a.com" + }; + + db.connect(); + db.query(`SELECT COUNT(*) FROM ${config.sql.table} WHERE USERNAME="${account.username}"`, (err, rows, fields) => { + if (err) { + res.status(500).json({ + status: "error", + error: "couldn't reach the database" + }); + } else if (rows[0].count > 0) { + res.status(409).json({ + status: "error", + error: "already exists" + }); + } else { + db.query(`INSERT INTO ${config.sql.table} (USERNAME, PASSWORD, EMAIL, GENDER) VALUES ("${account.username}", "${account.password}", "${account.email}", "N")`, (err, rows, fields) => { + if (err) { + res.status(500).json({ + status: "error", + error: "couldn't add the user" + }); + } else { + res.status(201).json({ + status: "success" + }); + } + }); + } + }); + + db.close(); +}); + + + +api.use((req, res, next) => { + res.status(404).json({ + status: "error", + error: "unknown endpoint" + }); +}); + +api.set("trust proxy", "loopback"); // only allow localhost to communicate with the API +api.listen(config.port, () => console.info(`Listening on port ${config.port}`)); -- cgit v1.2.3-60-g2f50