| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204 |
- const whiskers = require('whiskers');
- const fs = require('fs');
- const OriginModel = require('../models/origin.js').OriginModel;
- const ApiKeyModel = require('../models/apiKey.js').ApiKeyModel;
- const PasteContent = require('../models/pasteContent.js').PasteContent;
- const AccessModel = require('../models/access.js').AccessModel;
- const mCrypto = require('../src/crypto.js');
- const Security = require('../src/security.js');
- const CONFIG = require('../src/config.js');
- async function onAccessContent(app, req, entity) {
- const rel = "" + (req.body.rel || req.body.r || "");
- if (entity.privId !== req.params.id) {
- let accessEntry = new AccessModel(entity.privId, rel, Security.getRequestIp(req));
- await accessEntry.resolveIp();
- await app.databaseHelper.insertOne(accessEntry);
- }
- }
- async function renderRawPage(app, req, res, entity) {
- await onAccessContent(app, req, entity);
- if (entity.type === 'paste')
- return await app.routerUtils.staticServe(res, app.getData(entity.privId));
- else if (entity.type === 'file') {
- let data = JSON.parse(entity.data);
- return await app.routerUtils.staticDownload(res, app.getData(entity.privId), data.name, data.type);
- }
- else if (entity.type === 'short')
- return res.end(entity.data); // FIXME stats + ?rel
- app.routerUtils.onInternalError(res, "Unknown type: " +entity.type);
- }
- async function renderPublicPage(app, req, res, entity) {
- await onAccessContent(app, req, entity);
- if (entity.type === 'paste')
- return await app.routerUtils.staticServe(res, app.getData(entity.privId));
- else if (entity.type === 'file') {
- let data = JSON.parse(entity.data);
- // FIXME viewer
- return await app.routerUtils.staticDownload(res, app.getData(entity.privId), data.name, data.type);
- }
- else if (entity.type === 'short')
- return app.routerUtils.redirect(res, entity.data);
- app.routerUtils.onInternalError(res, "Unknown type: " +entity.type);
- }
- async function _readAccess(app, entityId) {
- return (await app.databaseHelper.fetch(AccessModel, { privId: entityId }))
- .map(x => x.describe())
- .map(x => { delete x.ipAddress; delete x.privId; x.ipRegion = JSON.parse(x.ipRegion); return x; });
- }
- async function renderPrivatePage(app, req, res, entity) {
- let stat;
- try { stat = fs.statSync(app.dataDir+entity.privId); } catch (e) { stat = { error: e }; }
- const access = await _readAccess(app, entity.privId);
- const origins = await app.databaseHelper.fetch(OriginModel, { privId: entity.privId });
- let context = app.routerUtils.commonRenderInfos();
- context.page_title += " - Pastit";
- entity.createdTime = entity.created.getTime();
- entity.expireTime = entity.expire?.getTime() || 0;
- res.end(whiskers.render(require('../templates/stats.js'), {
- ...context,
- ...{size: stat.size || 0},
- ...{ access: JSON.stringify(access)},
- ...{entity: entity},
- ...{origins: JSON.stringify(origins.map(i => i.name))}
- }));
- }
- module.exports = { register: app => {
- // Root
- app.router.get("/", (req, res) => {
- app.routerUtils.redirect(res, '/pastit');
- });
- // Access page
- app.router.get("/x/:id", async (req, res) => {
- let entity = await app.databaseHelper.findOne(PasteContent, { privId: req.params.id, publicId: req.params.id }, " or ");
- if (entity && entity.privId === req.params.id)
- return renderPrivatePage(app, req, res, entity);
- if (entity && !entity.expired)
- return renderPublicPage(app, req, res, entity);
- app.routerUtils.onPageNotFound(res);
- });
- app.router.get("/x/raw/:id", async (req, res) => {
- let entity = await app.databaseHelper.findOne(PasteContent, { privId: req.params.id, publicId: req.params.id }, " or ");
- if (entity && !entity.expired)
- return renderRawPage(app, req, res, entity);
- app.routerUtils.onPageNotFound(res);
- });
- // pastebin tool
- app.router.get("/pastit", (req, res) => {
- let context = app.routerUtils.commonRenderInfos();
- context.page_title += " - Pastit";
- res.end(whiskers.render(require('../templates/pastit.js'), context));
- });
- app.router.post("/pastit", async (req, res) => {
- const content = "" + (req.body.content || req.post);
- const privId = mCrypto.string(content);
- if (!CONFIG.reCaptchaSecret || req.body['g-recaptcha-response']) {
- const captchaOk = await Security.captchaCheck(req.body['g-recaptcha-response'], Security.getRequestIp(req));
- if (!captchaOk)
- return app.routerUtils.jsonResponse(res, { err: "Invalid captcha input", id: null });
- } else if (req.body['apiKey']) {
- if (!(await app.databaseHelper.findOne(ApiKeyModel, { apiKey: req.body['apiKey'] })))
- return app.routerUtils.jsonResponse(res, { err: "Unauthorized access", id: null });
- } else {
- return app.routerUtils.jsonResponse(res, { err: "Unauthorized access", id: null });
- }
- let entity = await app.databaseHelper.findOne(PasteContent, { privId: privId });
- if (!content || !content.length)
- return app.routerUtils.jsonResponse(res, { err: "Empty input", id: null });
- if (content.length > CONFIG.maxPastebinSize)
- return app.routerUtils.jsonResponse(res, { err: "Input size is too large", id: null });
- if (entity && !entity.expired) {
- entity.renew();
- await app.databaseHelper.update({privId: privId}, entity);
- } else {
- entity = entity || new PasteContent(privId, "paste", Security.getRequestIp(req));
- entity.expired = false;
- entity.apiKey = req.body['apiKey'] || null;
- entity.renew();
- fs.writeFileSync(app.getData(privId), content);
- await app.databaseHelper.upsertOne(entity);
- }
- if (req.body.apiKey)
- res.end(CONFIG.url+"/x/" +entity.publicId+"\r\n");
- else
- app.routerUtils.jsonResponse(res, { err: null, id: entity.publicId });
- });
- // URL shortener tool
- app.router.get("/short", (req, res) => {
- let context = app.routerUtils.commonRenderInfos();
- context.page_title += " - Shortener";
- res.end(whiskers.render(require('../templates/short.js'), context));
- });
- app.router.post("/short", async (req, res) => {
- const link = "" + req.body.content;
- const privId = mCrypto.string(await app.databaseHelper.count(PasteContent) + link);
- const captchaOk = await Security.captchaCheck(req.body['g-recaptcha-response'], Security.getRequestIp(req));
- if (!captchaOk)
- return app.routerUtils.jsonResponse(res, { err: "Invalid captcha input", id: null });
- if (!link || !link.length)
- return app.routerUtils.jsonResponse(res, { err: "Empty input", id: null });
- if (link.length > CONFIG.maxUrlSize)
- return app.routerUtils.jsonResponse(res, { err: "Input size is too large", id: null });
- entity = new PasteContent(privId, "short", Security.getRequestIp(req));
- entity.data = link;
- await app.databaseHelper.insertOne(entity);
- app.routerUtils.jsonResponse(res, { err: null, id: entity.privId });
- });
- // Files tool
- app.router.get("/files", (req, res) => {
- let context = app.routerUtils.commonRenderInfos();
- context.page_title += " - File host";
- res.end(whiskers.render(require('../templates/files.js'), context));
- });
- app.router.post("/files", async (req, res) => {
- const formData = (req.body["multipart-data"] || []).reduce((res, x) => { res[x.fieldName] = x; return res; }, {});
- const privId = mCrypto.string(await app.databaseHelper.count(PasteContent) + (req.content?.fileName || ""));
- const captchaOk = await Security.captchaCheck(formData["g-recaptcha-response"]?.fileData, req.headers['x-forwarded-for'] || req.socket.remoteAddress);
- if (!captchaOk)
- return app.routerUtils.jsonResponse(res, { err: "Invalid captcha input", id: null });
- if (!formData.content?.fileData || !formData.content.fileData.length)
- return app.routerUtils.jsonResponse(res, { err: "Empty input", id: null });
- if (formData.content.fileData.length > CONFIG.maxFileUploadSize)
- return app.routerUtils.jsonResponse(res, { err: "Input size is too large", id: null });
- const entity = new PasteContent(privId, "file", Security.getRequestIp(req));
- entity.data = JSON.stringify({ name: formData.content.fileName, type: formData.content.fileType });
- fs.writeFileSync(app.getData(privId), formData.content.fileData, {encoding: formData.content.fileType.indexOf('text') >= 0 ? 'utf8' : 'binary'});
- await app.databaseHelper.insertOne(entity);
- app.routerUtils.jsonResponse(res, { err: null, id: entity.privId });
- });
- // API page
- app.router.get("/api", (req, res) => {
- let context = app.routerUtils.commonRenderInfos();
- context.page_title += " - API Usage";
- res.end(whiskers.render(require('../templates/api.js'), context));
- });
- app.router.post("/api", async (req, res) => {
- const ipAddress = req.headers['x-forwarded-for'] || req.socket.remoteAddress;
- const captchaOk = await Security.captchaCheck(req.body['g-recaptcha-response'], Security.getRequestIp(req));
- if (!captchaOk)
- return app.routerUtils.jsonResponse(res, { err: "Invalid captcha input", id: null });
- const privKey = mCrypto.string(Date.now() + "" + await app.databaseHelper.count(ApiKeyModel) + "SALT_INPUT_API_KEY" +ipAddress);
- const model = new ApiKeyModel(privKey, ipAddress);
- await app.databaseHelper.insertOne(model);
- return app.routerUtils.jsonResponse(res, { err: null, id: privKey });
- });
- }};
|