From 238935ba54d9fa7e0cbd327ee3b7924e4b4a10ad Mon Sep 17 00:00:00 2001 From: CaramelKat <32065563+caramelkat@users.noreply.github.com> Date: Sun, 26 Apr 2020 00:42:52 -0500 Subject: [PATCH] added basic support for posts --- package-lock.json | 151 ++++++++++++++++++++++ package.json | 1 + src/database.js | 12 ++ src/models/post.js | 11 +- src/server.js | 2 +- src/services/miiverse-api/index.js | 14 +- src/services/miiverse-api/routes/index.js | 1 + src/services/miiverse-api/routes/post.js | 49 +++++++ src/util/CommunityPostGen.js | 101 +++++++++++++++ src/util/processHeaders.js | 15 +++ 10 files changed, 349 insertions(+), 8 deletions(-) create mode 100644 src/services/miiverse-api/routes/post.js create mode 100644 src/util/CommunityPostGen.js create mode 100644 src/util/processHeaders.js diff --git a/package-lock.json b/package-lock.json index e552e03..f4bac2a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -98,6 +98,12 @@ "negotiator": "0.6.2" } }, + "append-field": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz", + "integrity": "sha1-HjRA6RXwsSA9I3SOeO3XubW0PlY=", + "dev": true + }, "array-flatten": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", @@ -152,6 +158,48 @@ "resolved": "https://registry.npmjs.org/bson/-/bson-1.1.3.tgz", "integrity": "sha512-TdiJxMVnodVS7r0BdL42y/pqC9cL2iKynVwA0Ho3qbsQYr428veL3l7BQyuqiw+Q5SqqoT0m4srSY/BlZ9AxXg==" }, + "buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", + "dev": true + }, + "busboy": { + "version": "0.2.14", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-0.2.14.tgz", + "integrity": "sha1-bCpiLvz0fFe7vh4qnDetNseSVFM=", + "dev": true, + "requires": { + "dicer": "0.2.5", + "readable-stream": "1.1.x" + }, + "dependencies": { + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "dev": true + }, + "readable-stream": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", + "dev": true + } + } + }, "bytes": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", @@ -162,6 +210,18 @@ "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==" }, + "concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, "content-disposition": { "version": "0.5.3", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", @@ -225,6 +285,42 @@ "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" }, + "dicer": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/dicer/-/dicer-0.2.5.tgz", + "integrity": "sha1-WZbAhrszIYyBLAkL3cCc0S+stw8=", + "dev": true, + "requires": { + "readable-stream": "1.1.x", + "streamsearch": "0.1.2" + }, + "dependencies": { + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "dev": true + }, + "readable-stream": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", + "dev": true + } + } + }, "ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -452,6 +548,21 @@ "mime-db": "1.43.0" } }, + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "dev": true + }, + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } + }, "moment": { "version": "2.24.0", "resolved": "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz", @@ -568,11 +679,33 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" }, + "multer": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/multer/-/multer-1.4.2.tgz", + "integrity": "sha512-xY8pX7V+ybyUpbYMxtjM9KAiD9ixtg5/JkeKUTD6xilfDv0vzzOFcCp4Ljb1UU3tSOM3VTZtKo63OmzOrGi3Cg==", + "dev": true, + "requires": { + "append-field": "^1.0.0", + "busboy": "^0.2.11", + "concat-stream": "^1.5.2", + "mkdirp": "^0.5.1", + "object-assign": "^4.1.1", + "on-finished": "^2.3.0", + "type-is": "^1.6.4", + "xtend": "^4.0.0" + } + }, "negotiator": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "dev": true + }, "object-to-xml": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/object-to-xml/-/object-to-xml-2.0.0.tgz", @@ -782,6 +915,12 @@ "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" }, + "streamsearch": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz", + "integrity": "sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo=", + "dev": true + }, "string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", @@ -804,6 +943,12 @@ "mime-types": "~2.1.24" } }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", + "dev": true + }, "uid-safe": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz", @@ -851,6 +996,12 @@ "@oozcitak/infra": "1.0.4", "@oozcitak/util": "1.0.3" } + }, + "xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "dev": true } } } diff --git a/package.json b/package.json index 0b1c5bd..8c0cc24 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ "xmlbuilder2": "0.0.4" }, "devDependencies": { + "multer": "^1.4.2", "object-to-xml": "^2.0.0" } } diff --git a/src/database.js b/src/database.js index b4bb29a..4dde3e9 100644 --- a/src/database.js +++ b/src/database.js @@ -43,6 +43,18 @@ async function getTopicByCommunityID(communityID) { }); } +async function getCommunityByTitleID(title_id) { + verifyConnected(); + + if (typeof communityID !== 'string') { + return null; + } + + return TOPIC.findOne({ + title_id: title_id + }); +} + async function getDiscoveryHosts() { verifyConnected(); return ENDPOINT.findOne({ diff --git a/src/models/post.js b/src/models/post.js index e69a38d..0bce41c 100644 --- a/src/models/post.js +++ b/src/models/post.js @@ -2,6 +2,13 @@ const { Schema, model } = require('mongoose'); //just testing pull requests const PostSchema = new Schema({ body: String, + app_data: String, + painting: String, + screenshot: String, + search_key: { + type: [String], + default: undefined + }, community_id: String, country_id: Number, created_at: String, @@ -63,9 +70,9 @@ PostSchema.methods.downEmpathy = async function() { await this.save(); }; -const MiiversePost = model('MiiversePost', PostSchema); +const POST = model('POST', PostSchema); module.exports = { PostSchema, - MiiversePost + POST }; \ No newline at end of file diff --git a/src/server.js b/src/server.js index 1391f17..f36b9e0 100644 --- a/src/server.js +++ b/src/server.js @@ -1,5 +1,4 @@ process.title = 'Pretendo - Miiverse'; - const express = require('express'); const morgan = require('morgan'); const xmlparser = require('./middleware/xml-parser'); @@ -19,6 +18,7 @@ app.disable('x-powered-by'); logger.info('Setting up Middleware'); app.use(morgan('dev')); app.use(express.json()); + app.use(express.urlencoded({ extended: true })); diff --git a/src/services/miiverse-api/index.js b/src/services/miiverse-api/index.js index 8ee3336..163414c 100644 --- a/src/services/miiverse-api/index.js +++ b/src/services/miiverse-api/index.js @@ -9,24 +9,28 @@ const routes = require('./routes'); const router = express.Router(); // Router to handle the subdomain restriction -const miiverse = express.Router(); +const discovery = express.Router(); +const api = express.Router(); const portal = express.Router(); const ctr = express.Router(); // Create subdomains logger.info('[MIIVERSE] Creating \'discovery\' subdomain'); -router.use(subdomain('discovery.olv', miiverse)); +router.use(subdomain('discovery.olv', discovery)); +logger.info('[MIIVERSE] Creating \'api\' subdomain'); +router.use(subdomain('api.olv', api)); logger.info('[MIIVERSE] Creating \'portal\' subdomain'); router.use(subdomain('portal.olv', portal)); router.use(subdomain('ctr.olv', ctr)); logger.info('[MIIVERSE] Importing middleware'); -miiverse.use(sessionMiddleware); -miiverse.use(pnidMiddleware); +discovery.use(sessionMiddleware); +discovery.use(pnidMiddleware); // Setup routes -miiverse.use('/v1/endpoint', routes.DISCOVERY); +discovery.use('/v1/endpoint', routes.DISCOVERY); +api.use('/v1/posts', routes.POST); ctr.use('/', routes.CTR); module.exports = router; \ No newline at end of file diff --git a/src/services/miiverse-api/routes/index.js b/src/services/miiverse-api/routes/index.js index 7408483..a975430 100644 --- a/src/services/miiverse-api/routes/index.js +++ b/src/services/miiverse-api/routes/index.js @@ -1,4 +1,5 @@ module.exports = { DISCOVERY: require('./discovery'), CTR: require('./ctr'), + POST: require('./post'), }; \ No newline at end of file diff --git a/src/services/miiverse-api/routes/post.js b/src/services/miiverse-api/routes/post.js new file mode 100644 index 0000000..d7020ff --- /dev/null +++ b/src/services/miiverse-api/routes/post.js @@ -0,0 +1,49 @@ +var express = require('express'); +var router = express.Router(); +const moment = require('moment'); +const { POST } = require('../../../models/post'); +const processHeaders = require('../../../util/processHeaders'); +var multer = require('multer'); +var upload = multer(); + +/* GET post titles. */ +router.post('/', upload.none(), function (req, res, next) { + + let paramPack = req.get('x-nintendo-parampack'); + let paramPackData = processHeaders.data.decodeParamPack(paramPack); + /*let dec = Buffer.from(paramPack, "base64").toString("ascii"); + dec = dec.slice(1, -1).split("\\"); + const paramPackData = {}; + for (let i = 0; i < dec.length; i += 2) { + paramPackData[dec[i].trim()] = dec[i + 1].trim(); + }*/ + const creationDate = moment().format('YYYY-MM-DDTHH:MM:SS'); + const document = { + body: req.body.body, + app_data: req.body.app_data, + painting: req.body.painting, + screenshot: req.body.screenshot, + search_key: req.body.search_key, + community_id: req.body.community_id, + country_id: paramPackData.country_id, + created_at: creationDate, + feeling_id: req.body.feeling_id, + id: 0, + is_autopost: req.body.is_autopost, + is_spoiler: req.body.is_spoiler, + is_app_jumpable: req.body.is_app_jumpable, + language_id: req.body.language_id, + mii: "mii_string", + mii_face_url: "mii_face", + pid: 0, + platform_id: paramPackData.platform_id, + region_id: paramPackData.region_id, + screen_name: "unknown", + title_id: paramPackData.title_id, + }; + const newPost = new POST(document); + newPost.save(); + res.sendStatus(200); +}); + +module.exports = router; \ No newline at end of file diff --git a/src/util/CommunityPostGen.js b/src/util/CommunityPostGen.js new file mode 100644 index 0000000..48a76a1 --- /dev/null +++ b/src/util/CommunityPostGen.js @@ -0,0 +1,101 @@ +const xmlbuilder = require("xmlbuilder"); +const moment = require("moment-timezone"); + +class CommunityPostGen { + /* TODO lots of stubs and constants in here */ + static async PostsResponse(posts, community) { + let xml = xmlbuilder.create("result") + .e("has_error", "0").up() + .e("version", "1").up() + .e("request_name", "posts").up() + .e("topic") + .e("community_id", community.id).up() + .up() + .e("posts"); + for (let i = 0; i < posts.length; i++) { + xml = xml.e("post") + .e("app_data", posts[i].appData).up() + .e("body", posts[i].body).up() + .e("community_id", community.id).up() + .e("country_id", "254").up() + .e("created_at", moment(posts[i].created).tz("GMT").format("YYYY-MM-DD hh:mm:ss")).up() + .e("feeling_id", "1").up() + .e("id", posts[i].id).up() + .e("is_autopost", "0").up() + .e("is_community_private_autopost", "0").up() + .e("is_spoiler", "0").up() + .e("is_app_jumpable", "0").up() + .e("empathy_count", posts[i].empathy).up() + .e("language_id", "1").up() + .e("number", "0").up(); + if (posts[i].painting) { + xml = xml.e("painting") + .e("format", "tga").up() + .e("content", posts[i].painting).up() + .e("size", posts[i].paintingSz).up() + .e("url", "https://s3.amazonaws.com/olv-public/pap/WVW69koebmETvBVqm1").up() + .up(); + } + xml = xml.e("pid", posts[i].pid).up() + .e("platform_id", "1").up() + .e("region_id", "4").up() + .e("reply_count", "0").up() + .e("screen_name", posts[i].screenName).up() + .e("title_id", posts[i].tid).up() + .up(); + } + + return xml.end({ pretty: true, allowEmpty: true }); + } + + static async EmptyResponse() { + const xml = xmlbuilder.create("result") + .e("has_error", "0").up() + .e("version", "1").up(); + return xml.end({ pretty: true, allowEmpty: true }); + } + + /* TODO Again, some constants */ + static async SinglePostResponse(post) { + let xml = xmlbuilder.create("result") + .e("has_error", "0").up() + .e("version", "1").up() + .e("post"); + if (post.appData) { + xml = xml.e("app_data", post.appData).up(); + } + xml = xml.e("body", post.body).up() + .e("community_id", post.communityID).up() + .e("country_id", "254").up() + .e("created_at", moment(post.created).tz("GMT").format("YYYY-MM-DD hh:mm:ss")).up() + .e("feeling_id", "1").up() + .e("id", post.id).up() + .e("is_autopost", "0").up() + .e("is_community_private_autopost", "0").up() + .e("is_spoiler", "0").up() + .e("is_app_jumpable", "0").up() + .e("empathy_count", post.empathy).up() + .e("language_id", "1").up() + .e("number", "0").up(); + if (post.painting) { + xml = xml.e("painting") + .e("format", "tga").up() + .e("content", post.painting).up() + .e("size", post.paintingSz).up() + .e("url", "https://s3.amazonaws.com/olv-public/pap/WVW69koebmETvBVqm1").up() + .up(); + } + xml = xml.e("pid", post.pid).up() + .e("platform_id", "1").up() + .e("region_id", "4").up() + .e("reply_count", "0").up() + .e("screen_name", post.screenName).up() + .e("title_id", post.tid).up() + .up(); + return xml.end({ pretty: true, allowEmpty: true }); + } +} + +if (typeof module !== "undefined") { + module.exports = CommunityPostGen; +} diff --git a/src/util/processHeaders.js b/src/util/processHeaders.js new file mode 100644 index 0000000..82036bc --- /dev/null +++ b/src/util/processHeaders.js @@ -0,0 +1,15 @@ +let methods = { + decodeParamPack: function (paramPack) { + /* Decode base64 */ + let dec = Buffer.from(paramPack, "base64").toString("ascii"); + /* Remove starting and ending '/', split into array */ + dec = dec.slice(1, -1).split("\\"); + /* Parameters are in the format [name, val, name, val]. Copy into out{}. */ + const out = {}; + for (let i = 0; i < dec.length; i += 2) { + out[dec[i].trim()] = dec[i + 1].trim(); + } + return out; + } +}; +exports.data = methods;