/* * Copyright ©️ 2018 Galt•Space Society Construction and Terraforming Company * (Founded by [Nikolai Popeka](https://github.com/npopeka), * [Dima Starodubcev](https://github.com/xhipster), * [Valery Litvin](https://github.com/litvintech) by * [Basic Agreement](http://cyb.ai/QmSAWEG5u5aSsUyMNYuX2A2Eaz4kEuoYWUkVBRdmu9qmct:ipfs)). * * Copyright ©️ 2018 Galt•Core Blockchain Company * (Founded by [Nikolai Popeka](https://github.com/npopeka) and * Galt•Space Society Construction and Terraforming Company by * [Basic Agreement](http://cyb.ai/QmaCiXUmSrP16Gz8Jdzq6AJESY1EAANmmwha15uR3c1bsS:ipfs)). */ import {IGeesomeApp} from "../../app/interface"; import {CorePermissionName} from "../../database/interface"; const config = require('./config'); const _ = require('lodash'); const mime = require('mime'); const pIteration = require('p-iteration'); const bodyParser = require('body-parser'); const busboy = require('connect-busboy'); const bearerToken = require('express-bearer-token'); // const proxy = require('express-http-proxy'); const request = require('request'); const service = require('restana')({ ignoreTrailingSlash: true, maxParamLength: 2000 }); const maxBodySizeMb = 2000; module.exports = async (geesomeApp: IGeesomeApp, port) => { require('./showEndpointsTable'); service.use(bodyParser.json({limit: maxBodySizeMb + 'mb'})); service.use(bodyParser.urlencoded({extended: true})); service.use(bearerToken()); service.use(require('morgan')('combined')); service.use(async (req, res, next) => { setHeaders(res); req.query = {}; if (_.includes(req.url, '?')) { const searchParams: any = new URLSearchParams(req.url.split('?')[1]); const keys = searchParams.keys(); for (let key = keys.next(); key.done !== true; key = keys.next()) { req.query[key.value] = searchParams.get(key.value); } } res.redirect = (url) => { //https://github.com/jkyberneees/ana/issues/16 res.send('', 301, { Location: encodeURI(url) }); }; if ( (_.startsWith(req.url, '/v1/user') || _.startsWith(req.url, '/v1/group') || _.startsWith(req.url, '/v1/admin')) && !_.startsWith(req.url, '/v1/login') && req.method !== 'OPTIONS' && req.method !== 'HEAD' ) { if (!req.token) { return res.send({ error: "Need authorization token", errorCode: 1 }, 401); } req.user = await geesomeApp.getUserByApiKey(req.token); if (!req.user) { return res.send({ error: "Incorrect api token", errorCode: 2 }, 403); } } next(); }); service.use(busboy()); service.options("/*", function (req, res, next) { setHeaders(res); res.send(200); }); service.head("/*", function (req, res, next) { setHeaders(res); res.send(200); }); service.get('/v1', async (req, res) => { const endpoints = config.endpointsInfo; const html = "" + endpoints .map(e => `

${e.uri}

Method: GET
Header: ${e.header}
Body: ${e.body}
Response: ${e.response}`) .join('

') + ""; res.send(html, 200); }); service.get('/v1/is-empty', async (req, res) => { res.send({ result: (await geesomeApp.database.getUsersCount()) === 0 }, 200); }); service.post('/v1/setup', async (req, res) => { if ((await geesomeApp.database.getUsersCount()) > 0) { return res.send(403); } const adminUser = await geesomeApp.registerUser(req.body.email, req.body.name, req.body.password); await pIteration.forEach(['AdminRead', 'AdminAddUser', 'AdminSetUserLimit', 'AdminAddUserApiKey', 'AdminSetPermissions', 'AdminAddBootNode', 'AdminRemoveBootNode'], (permissionName) => { return geesomeApp.database.addCorePermission(adminUser.id, CorePermissionName[permissionName]) }); res.send({user: adminUser, apiKey: await geesomeApp.generateUserApiKey(adminUser.id, "password_auth")}, 200); }); service.post('/v1/login', async (req, res) => { geesomeApp.loginUser(req.body.username, req.body.password).then(async user => { if (user) { return res.send({user, apiKey: await geesomeApp.generateUserApiKey(user.id, "password_auth")}, 200); } else { return res.send(403); } }).catch((err) => { console.error(err); res.send(403) }); }); service.get('/v1/user', async (req, res) => { if (!req.user || !req.user.id) { return res.send(401); } res.send(req.user, 200); }); service.post('/v1/admin/add-user', async (req, res) => { if (!await geesomeApp.database.isHaveCorePermission(req.user.id, CorePermissionName.AdminAddUser)) { return res.send(403); } res.send(await geesomeApp.registerUser(req.body.email, req.body.name, req.body.password)); }); service.post('/v1/admin/add-user-api-key', async (req, res) => { if (!await geesomeApp.database.isHaveCorePermission(req.user.id, CorePermissionName.AdminAddUserApiKey)) { return res.send(403); } res.send(await geesomeApp.generateUserApiKey(req.body.userId, 'admin_manual')); }); service.post('/v1/admin/set-user-limit', async (req, res) => { if (!await geesomeApp.database.isHaveCorePermission(req.user.id, CorePermissionName.AdminSetUserLimit)) { return res.send(403); } res.send(await geesomeApp.setUserLimit(req.user.id, req.body)); }); service.get('/v1/user/permissions/core/is-have/:permissionName', async (req, res) => { res.send({result: await geesomeApp.database.isHaveCorePermission(req.user.id, req.params.permissionName)}); }); service.post('/v1/admin/permissions/core/add_permission', async (req, res) => { if (!await geesomeApp.database.isHaveCorePermission(req.user.id, CorePermissionName.AdminSetPermissions)) { return res.send(403); } res.send(await geesomeApp.database.addCorePermission(req.body.userId, req.body.permissionName)); }); service.post('/v1/admin/permissions/core/remove_permission', async (req, res) => { if (!await geesomeApp.database.isHaveCorePermission(req.user.id, CorePermissionName.AdminSetPermissions)) { return res.send(403); } res.send(await geesomeApp.database.removeCorePermission(req.body.userId, req.body.permissionName)); }); service.get('/v1/admin/all-users', async (req, res) => { res.send(await geesomeApp.getAllUserList(req.user.id, req.query.search, _.pick(req.query, ['sortBy', 'sortDir', 'limit', 'offset']))); }); service.get('/v1/admin/all-content', async (req, res) => { res.send(await geesomeApp.getAllContentList(req.user.id, req.query.search, _.pick(req.query, ['sortBy', 'sortDir', 'limit', 'offset']))); }); service.get('/v1/admin/all-groups', async (req, res) => { res.send(await geesomeApp.getAllGroupList(req.user.id, req.query.search, _.pick(req.query, ['sortBy', 'sortDir', 'limit', 'offset']))); }); service.get('/v1/admin/boot-nodes', async (req, res) => { if (!await geesomeApp.database.isHaveCorePermission(req.user.id, CorePermissionName.AdminRead)) { return res.send(403); } res.send(await geesomeApp.storage.getBootNodeList()); }); service.post('/v1/admin/boot-nodes/add', async (req, res) => { if (!await geesomeApp.database.isHaveCorePermission(req.user.id, CorePermissionName.AdminAddBootNode)) { return res.send(403); } res.send(await geesomeApp.storage.addBootNode(req.body.address)); }); service.post('/v1/admin/boot-nodes/remove', async (req, res) => { if (!await geesomeApp.database.isHaveCorePermission(req.user.id, CorePermissionName.AdminRemoveBootNode)) { return res.send(403); } res.send(await geesomeApp.storage.removeBootNode(req.body.address)); }); service.get('/v1/node-address-list', async (req, res) => { res.send({result: await geesomeApp.storage.nodeAddressList()}); }); service.get('/v1/admin/get-user/:userId/limit/:limitName', async (req, res) => { res.send(await geesomeApp.getUserLimit(req.user.id, req.params.userId, req.params.limitName)); }); service.post('/v1/user/update', async (req, res) => { res.send(await geesomeApp.updateUser(req.user.id, req.body)); }); service.post('/v1/user/create-group', async (req, res) => { res.send(await geesomeApp.createGroup(req.user.id, req.body), 200); }); service.post('/v1/user/group/:groupId/update', async (req, res) => { res.send(await geesomeApp.updateGroup(req.user.id, req.params.groupId, req.body), 200); }); service.get('/v1/user/member-in-groups', async (req, res) => { res.send(await geesomeApp.getMemberInGroups(req.user.id)); }); service.get('/v1/user/admin-in-groups', async (req, res) => { res.send(await geesomeApp.getAdminInGroups(req.user.id)); }); service.get('/v1/user/group/:groupId/can-create-post', async (req, res) => { res.send({valid: await geesomeApp.canCreatePostInGroup(req.user.id, req.params.groupId)}); }); service.get('/v1/user/group/:groupId/can-edit', async (req, res) => { res.send({valid: await geesomeApp.canEditGroup(req.user.id, req.params.groupId)}); }); service.post('/v1/user/group/:groupId/create-post', async (req, res) => { if (!await geesomeApp.canCreatePostInGroup(req.user.id, req.params.groupId)) { return res.send(403); } res.send(await geesomeApp.createPost(req.user.id, req.body), 200); }); service.post('/v1/user/group/:groupId/update-post/:postId', async (req, res) => { if (!await geesomeApp.canCreatePostInGroup(req.user.id, req.params.groupId)) { return res.send(403); } res.send(await geesomeApp.updatePost(req.user.id, req.params.postId, req.body), 200); }); service.post('/v1/user/group/:groupId/is-member', async (req, res) => { res.send({result: await geesomeApp.isMemberInGroup(req.user.id, req.params.groupId)}, 200); }); service.post('/v1/user/group/:groupId/join', async (req, res) => { //TODO: check for private group res.send(await geesomeApp.addMemberToGroup(req.user.id, req.params.groupId), 200); }); service.post('/v1/user/group/:groupId/leave', async (req, res) => { res.send(await geesomeApp.removeMemberFromGroup(req.user.id, req.params.groupId), 200); }); service.get('/v1/user/api-keys', async (req, res) => { res.send(await geesomeApp.getUserApiKeys(req.user.id, req.query.isDisabled, req.query.search, _.pick(req.query, ['sortBy', 'sortDir', 'limit', 'offset'])), 200); }); service.post('/v1/user/save-file', async (req, res) => { req.pipe(req.busboy); const body = {}; req.busboy.on('field', function (fieldname, val, fieldnameTruncated, valTruncated, encoding, mimetype) { body[fieldname] = val; }); req.busboy.on('file', async function (fieldname, file, filename) { res.send(await geesomeApp.saveData(file, filename, { userId: req.user.id, apiKey: req.token, groupId: body['groupId'], folderId: body['folderId'] }), 200); }); }); service.post('/v1/user/save-data', async (req, res) => { res.send(await geesomeApp.saveData(req.body['content'], req.body['fileName'] || req.body['name'], { userId: req.user.id, apiKey: req.token, groupId: req.body['groupId'], folderId: req.body['folderId'] }), 200); }); service.post('/v1/user/save-data-by-url', async (req, res) => { res.send(await geesomeApp.saveDataByUrl(req.body['url'], { userId: req.user.id, apiKey: req.token, groupId: req.body['groupId'], driver: req.body['driver'], folderId: req.body['folderId'] }), 200); }); service.get('/v1/user/file-catalog/', async (req, res) => { res.send(await geesomeApp.getFileCatalogItems(req.user.id, req.query.parentItemId, req.query.type, req.query.search, _.pick(req.query, ['sortBy', 'sortDir', 'limit', 'offset']))); }); service.get('/v1/user/file-catalog/file-catalog-item/:itemId/breadcrumbs', async (req, res) => { res.send(await geesomeApp.getFileCatalogItemsBreadcrumbs(req.user.id, req.params.itemId)); }); service.post('/v1/user/file-catalog/create-folder', async (req, res) => { res.send(await geesomeApp.createUserFolder(req.user.id, req.body.parentItemId, req.body.name)); }); service.post('/v1/user/file-catalog/add-content-to-folder', async (req, res) => { res.send(await geesomeApp.addContentToFolder(req.user.id, req.body.contentId, req.body.folderId)); }); service.post('/v1/user/file-catalog/file-catalog-item/:itemId/update', async (req, res) => { res.send(await geesomeApp.updateFileCatalogItem(req.user.id, req.params.itemId, req.body)); }); service.post('/v1/file-catalog/get-contents-ids', async (req, res) => { res.send(await geesomeApp.getContentsIdsByFileCatalogIds(req.body)); }); service.get('/v1/group/:groupId', async (req, res) => { res.send(await geesomeApp.getGroup(req.params.groupId)); }); service.get('/v1/group/:groupId/posts', async (req, res) => { res.send(await geesomeApp.getGroupPosts(req.params.groupId, _.pick(req.query, ['sortBy', 'sortDir', 'limit', 'offset']))); }); service.get('/v1/group/:groupId/peers', async (req, res) => { res.send(await geesomeApp.getGroupPeers(req.params.groupId)); }); service.get('/v1/content/:contentId', async (req, res) => { res.send(await geesomeApp.getContent(req.params.contentId)); }); service.get('/v1/content-data/:storageId', async (req, res) => { geesomeApp.getFileStream(req.params.storageId).then((stream) => { stream.pipe(res); }) }); service.get('/ipfs/:storageId', async (req, res) => { //TODO: https://gist.github.com/padenot/1324734 geesomeApp.getFileStream(req.params.storageId).then((stream) => { stream.pipe(res); }) }); service.get('/resolve/:storageId', async (req, res) => { geesomeApp.resolveStaticId(req.params.storageId).then(res.send.bind(res)).catch((err) => { res.send(err.message, 500) }) }); service.get('/ipld/*', async (req, res) => { const ipldPath = req.url.replace('/ipld/', ''); console.log('ipldPath', ipldPath); geesomeApp.getDataStructure(ipldPath).then(result => { res.send(_.isNumber(result) ? result.toString() : result); }).catch(() => { res.send(null, 200) }); }); service.get('/api/v0/refs*', (req, res) => { request('http://localhost:5002/api/v0/refs' + req.url.split('/api/v0/refs')[1]).pipe(res); }); service.post('/save-object', async (req, res) => { geesomeApp.storage.saveObject(req.body).then((result) => { res.send(result); }).catch(() => { res.send(null, 500) }); }); if (geesomeApp.frontendStorageId) { service.get('/node*', async (req, res) => { if (req.url === '/node') { return res.redirect('/node/'); } let path = req.url.replace('/node', ''); if (!path || path === '/') { path = '/index.html'; } res.setHeader('Content-Type', mime.getType(path)); geesomeApp.getFileStream(geesomeApp.frontendStorageId + path).then((stream) => { stream.pipe(res); }) }); } function handleError(res, e) { return res.send({ error: e.message || e, errorCode: -1 }, 400); } function setHeaders(res) { res.setHeader('Strict-Transport-Security', 'max-age=0'); res.setHeader('Access-Control-Allow-Credentials', 'true'); res.setHeader('Access-Control-Allow-Origin', '*'); res.setHeader('Access-Control-Allow-Methods', "GET, POST, PATCH, PUT, DELETE, OPTIONS, HEAD"); res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization, Content-Length, X-Requested-With'); } console.log('🚀 Start api on port', port); return service.start(port); };