diff --git a/.env.dist b/.env.dist index 5a64939..eef3711 100644 --- a/.env.dist +++ b/.env.dist @@ -1,7 +1,6 @@ DEBUG=true PUBLIC_URL=http://localhost:1337 APP_PORT=1337 -SANDBOX_PORT=1338 PARSE_APP_NAME=connect PARSE_APP_ID=connect diff --git a/docs/usage.md b/docs/usage.md index 2106c1a..264c629 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -426,13 +426,9 @@ The response from batch will be a list with the same number of elements as the i ### Sandbox Before use the production database you can use the sanbox environnement. +Just add the following header to every requests you make to `/classes/*` or `/batch`: -For that two changes should be done : - -- Every endpoint uri is `https://connect-project.io/parse-sandbox` instead of `https://connect-project.io/parse` -- Change the header value `x-parse-application-id` to `connect-sandbox`. - -For the authentication you have to use the APP_TOKEN_SANDBOX instead of the APP_TOKEN. +`x-is-sandbox: true` ## Schema Contribute diff --git a/ecosystem.config.js b/ecosystem.config.js index 8d83e9d..ef9a35e 100644 --- a/ecosystem.config.js +++ b/ecosystem.config.js @@ -4,13 +4,7 @@ module.exports = { name: 'connect', script: 'src/index.js', watch: '.', - ignore_watch : ["node_modules", "logs"], - }, - { - name: 'connect-sandbox', - script: 'src/sandbox.js', - watch: '.', - ignore_watch : ["node_modules", "logs"], + ignore_watch: ['node_modules', 'logs'], }, ], }; diff --git a/package.json b/package.json index d1c55d9..b887f3c 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,6 @@ "description": "An open platform to save anonymous data coming from any application using this API", "scripts": { "start": "node src/index.js", - "start.sandbox": "node src/sandbox.js", "build": "webpack --mode production", "build.dev": "webpack --mode development", "eslint.check": "eslint --ext js,jsx .", @@ -12,7 +11,6 @@ "prettier.check": "prettier --list-different .", "prettier.fix": "prettier --write .", "test": "jest --config spec/jest.config.js -i --forceExit --detectOpenHandles", - "copyToSandbox": "./bin/migration.sh", "performance": "node bin/performance.js" }, "eslintConfig": { diff --git a/prod_deploy/.env b/prod_deploy/.env index 03381b1..2c38787 100644 --- a/prod_deploy/.env +++ b/prod_deploy/.env @@ -3,7 +3,6 @@ VERSION_NUMBER=1.0.0 # Set all domain value if it need to be change PUBLIC_URL=https://connect-project.io/ APP_PORT=3000 -SANDBOX_PORT=3001 # Setup your custom key (a random string value) PARSE_FILE_KEY=TOSETUP diff --git a/prod_deploy/README.md b/prod_deploy/README.md index f543248..770732e 100644 --- a/prod_deploy/README.md +++ b/prod_deploy/README.md @@ -23,10 +23,6 @@ - `var passwd = "connect"` - `use connect` - `db.createUser({user: user, pwd: passwd, roles: ["readWrite"]});` - - `connect-api` - - `db.createUser({user: user, pwd: passwd, roles: ["readWrite"]});` - - `connect-sandbox` - - `db.createUser({user: user, pwd: passwd, roles: ["readWrite"]});` - Installer node et yarn (suivre https://classic.yarnpkg.com/en/docs/install/#centos-stable @@ -64,7 +60,9 @@ - `curl -v localhost:3001/parse-sandbox` ## Configure crontab to renew the certificate + - run `sudo crontab -e` and add the following: + ``` PATH=/sbin:/bin:/usr/sbin:/usr/bin @@ -72,7 +70,9 @@ PATH=/sbin:/bin:/usr/sbin:/usr/bin ``` ## Installation de webhook + Configuration de webhook pour effectuer la mise à jour du serveur de manière automatique + - `sudo mkdir /opt/webhook` - `sudo chown $USER /opt/webhook` - Download release from https://github.com/adnanh/webhook/releases and extract the `webhook` binary to `/opt/webhook` @@ -85,11 +85,13 @@ Configuration de webhook pour effectuer la mise à jour du serveur de manière a - `curl localhost:9990/hooks/deploy-connect` should answer: `Hook rules were not satisfied.` - `sudo systemctl enable webhook` - Add the following redirection to the nginx server: + ``` location /hooks/ { proxy_pass http://127.0.0.1:9990; } ``` + - `sudo nginx -s reload` ## Pare-feu diff --git a/spec/__mock__/config.js b/spec/__mock__/config.js index 02b34b1..a670b6a 100644 --- a/spec/__mock__/config.js +++ b/spec/__mock__/config.js @@ -3,8 +3,6 @@ module.exports = { PUBLIC_URL: 'http://localhost:3000', API_URL: 'http://localhost:3000', APP_PORT: '3000', - // SANDBOX_URL: 'http://localhost:3000', - // SANDBOX_PORT: '3000', PARSE_APP_NAME: 'test', PARSE_APP_ID: 'test', PARSE_FILE_KEY: 'test', @@ -12,7 +10,6 @@ module.exports = { PARSE_READONLY_MASTER_KEY: 'test_masterkey_readonly', PARSE_DASHBOARD_MAINTENER_PWD: 'test', PARSE_DASHBOARD_ROOT_PWD: 'test', - PARSE_SANDBOX: false, PARSE_SILENT: true, MONGO_URI: 'mongodb://test:test@localhost:27017/test', AUTH_SECRET: 'test', diff --git a/src/config/index.js b/src/config/index.js index 7e2dbb5..d6a6ead 100644 --- a/src/config/index.js +++ b/src/config/index.js @@ -21,11 +21,6 @@ testConfig(APP_PORT, 'APP_PORT'); const API_URL = `http://localhost:${APP_PORT}`; -const { SANDBOX_PORT } = process.env; -testConfig(SANDBOX_PORT, 'SANDBOX_PORT'); - -const SANDBOX_URL = `http://localhost:${SANDBOX_PORT}`; - const { PARSE_APP_NAME } = process.env; testConfig(PARSE_APP_NAME, 'PARSE_APP_NAME'); @@ -81,8 +76,6 @@ module.exports = { PUBLIC_URL, API_URL, APP_PORT, - SANDBOX_URL, - SANDBOX_PORT, PARSE_APP_NAME, PARSE_APP_ID, PARSE_FILE_KEY, @@ -90,7 +83,6 @@ module.exports = { PARSE_READONLY_MASTER_KEY, PARSE_DASHBOARD_MAINTENER_PWD, PARSE_DASHBOARD_ROOT_PWD, - PARSE_SANDBOX: false, PARSE_SILENT: false, MONGO_URI, AUTH_SECRET, diff --git a/src/connectServer.js b/src/connectServer.js index 6ce5cba..e49c5d3 100644 --- a/src/connectServer.js +++ b/src/connectServer.js @@ -8,9 +8,10 @@ const oauthApi = require('./oauth/oauth-routes'); const parseApi = require('./middleware/parse'); const parseDashboard = require('./middleware/parseDashboard'); const parseSwagger = require('./middleware/parseSwagger'); +const sandboxMiddleware = require('./middleware/sandboxMiddleware'); +const oauthMiddleware = require('./oauth/oauth-middleware'); const configFront = require('./config/front'); -const oauthMiddleware = require('./oauth/oauth-middleware'); class ConnectServer { constructor(app, server) { @@ -42,6 +43,9 @@ class ConnectServer { app.use('/parse/batch', oauthMiddleware); app.use('/parse/classes', oauthMiddleware); + app.use('/parse/batch', sandboxMiddleware); + app.use('/parse/classes', sandboxMiddleware); + // Serve the Parse API at /parse URL prefix const parseMiddleware = await parseApi(parseCloudEvent); app.use('/parse', parseMiddleware.app); diff --git a/src/db/client.js b/src/db/client.js deleted file mode 100644 index 29f84e1..0000000 --- a/src/db/client.js +++ /dev/null @@ -1,31 +0,0 @@ -const mongoose = require('mongoose'); -const { MONGO_URI } = require('../config'); - -mongoose.set('useFindAndModify', false); -mongoose.set('useCreateIndex', true); -mongoose.set('useNewUrlParser', true); -mongoose.set('useUnifiedTopology', true); - -let parseConnect; -let apiConnect; -let parseSandboxConnect; - -module.exports = () => { - if (!parseConnect) { - parseConnect = mongoose.createConnection(MONGO_URI); - } - - if (!apiConnect) { - apiConnect = mongoose.createConnection(`${MONGO_URI}-api`); - } - - if (!parseSandboxConnect) { - parseSandboxConnect = mongoose.createConnection(`${MONGO_URI}-sandbox`); - } - - return { - parseConnect, - apiConnect, - parseSandboxConnect, - }; -}; diff --git a/src/db/model/application.js b/src/db/model/application.js deleted file mode 100644 index b8ed426..0000000 --- a/src/db/model/application.js +++ /dev/null @@ -1,25 +0,0 @@ -const mongoose = require('mongoose'); - -module.exports = (apiConnect) => { - if (apiConnect.modelNames().includes('Application')) { - return apiConnect.model('Application'); - } - - const applicationSchema = new mongoose.Schema({ - developer: { - type: mongoose.Schema.Types.ObjectId, - ref: 'Developer', - }, - name: String, - description: String, - parse_name: { type: String, unique: true }, - token: String, - token_sandbox: String, - apple_store_link: String, - google_market_link: String, - create_at: { type: Date, default: Date.now }, - updated_at: { type: Date, default: Date.now }, - }); - - return apiConnect.model('Application', applicationSchema); -}; diff --git a/src/db/model/developer.js b/src/db/model/developer.js deleted file mode 100644 index 3ec0926..0000000 --- a/src/db/model/developer.js +++ /dev/null @@ -1,18 +0,0 @@ -const mongoose = require('mongoose'); - -module.exports = (apiConnect) => { - if (apiConnect.modelNames().includes('Developer')) { - return apiConnect.model('Developer'); - } - - const developerSchema = new mongoose.Schema({ - login: String, - github_id: { type: Number, unique: true }, - company_name: String, - email: String, - create_at: { type: Date, default: Date.now }, - updated_at: { type: Date, default: Date.now }, - }); - - return apiConnect.model('Developer', developerSchema); -}; diff --git a/src/db/model/index.js b/src/db/model/index.js deleted file mode 100644 index 1d07680..0000000 --- a/src/db/model/index.js +++ /dev/null @@ -1,12 +0,0 @@ -const connectClient = require('../client'); -const Developer = require('./developer'); -const Application = require('./application'); - -module.exports = () => { - const { apiConnect } = connectClient(); - - return { - Developer: Developer(apiConnect), - Application: Application(apiConnect), - }; -}; diff --git a/src/db/parse.js b/src/db/parse.js deleted file mode 100644 index 0455901..0000000 --- a/src/db/parse.js +++ /dev/null @@ -1,56 +0,0 @@ -const mongoose = require('mongoose'); -const getClasses = require('../parse/schema/getClasses'); -const connectClient = require('./client'); - -const modelMapping = new Map(); -const modeSandboxMapping = new Map(); - -// Generate all mongoose schema and model for the parse db -// eslint-disable-next-line max-statements -const getParseModel = async function (sandbox) { - const schemaClasses = await getClasses(); - const { parseConnect, parseSandboxConnect } = connectClient(); - const connect = sandbox ? parseSandboxConnect : parseConnect; - const models = sandbox ? modeSandboxMapping : modelMapping; - - if (models.size > 0) { - return models; - } - - for (const schemaClass of schemaClasses) { - const schema = new mongoose.Schema({ - _p_owner: String, - }); - - models.set( - schemaClass.className, - connect.model(schemaClass.className, schema, schemaClass.className), - ); - } - - const userSchema = new mongoose.Schema({ - _id: String, - username: String, - }); - - models.set('_User', connect.model('_User', userSchema, '_User')); - - const sessionSchema = new mongoose.Schema({ - _p_user: String, - }); - - models.set('_Session', connect.model('_Session', sessionSchema, '_Session')); - - const joinUsersSchema = new mongoose.Schema({ - relatedId: String, - }); - - models.set( - '_Join:users:_Role', - connect.model('_Join:users:_Role', joinUsersSchema, '_Join:users:_Role'), - ); - - return models; -}; - -module.exports = getParseModel; diff --git a/src/middleware/parseDashboard.js b/src/middleware/parseDashboard.js index 48aa7ca..ba6fa6e 100644 --- a/src/middleware/parseDashboard.js +++ b/src/middleware/parseDashboard.js @@ -19,13 +19,6 @@ module.exports = () => readOnlyMasterKey: PARSE_READONLY_MASTER_KEY, appName: PARSE_APP_NAME, }, - { - serverURL: `${PUBLIC_URL}/parse-sandbox`, - appId: `${PARSE_APP_ID}-sandbox`, - masterKey: PARSE_MASTER_KEY, - readOnlyMasterKey: PARSE_READONLY_MASTER_KEY, - appName: `${PARSE_APP_NAME}-sandbox`, - }, ], users: [ { diff --git a/src/middleware/parseSandbox.js b/src/middleware/parseSandbox.js deleted file mode 100644 index 4e3b3e5..0000000 --- a/src/middleware/parseSandbox.js +++ /dev/null @@ -1,28 +0,0 @@ -const { ParseServer } = require('parse-server'); -const { - SANDBOX_URL, - MONGO_URI, - PARSE_APP_NAME, - PARSE_APP_ID, - PARSE_FILE_KEY, - PARSE_MASTER_KEY, - PARSE_READONLY_MASTER_KEY, -} = require('../config'); -const cloud = require('../parse/cloud/main'); - -module.exports = () => - new ParseServer({ - databaseURI: `${MONGO_URI}-sandbox`, - cloud: (Parse) => { - cloud(Parse); - }, - appId: `${PARSE_APP_ID}-sandbox`, - fileKey: PARSE_FILE_KEY, - masterKey: PARSE_MASTER_KEY, - readOnlyMasterKey: PARSE_READONLY_MASTER_KEY, - appName: `${PARSE_APP_NAME}-sandbox`, - allowClientClassCreation: false, - enableAnonymousUsers: false, - maxLimit: 100, - serverURL: `${SANDBOX_URL}/parse-sandbox`, - }); diff --git a/src/middleware/sandboxMiddleware.js b/src/middleware/sandboxMiddleware.js new file mode 100644 index 0000000..8537049 --- /dev/null +++ b/src/middleware/sandboxMiddleware.js @@ -0,0 +1,41 @@ +/* eslint-disable max-statements */ + +const changeUrl = (req, toReplace, replacement) => { + req.url = req.url.replace(toReplace, replacement); + req.originalUrl = req.originalUrl.replace(toReplace, replacement); + req.path = req.path.replace(toReplace, replacement); +}; + +const transformPathForSandbox = (originalPath) => { + const regexpResult = originalPath.match(/\/parse\/classes\/([a-zA-Z_]+)/); + if (regexpResult.length > 1) { + return '/parse/classes/Sandbox_' + regexpResult[1]; + } +}; + +module.exports = (req, res, next) => { + if (req.headers['x-is-sandbox'] === 'true') { + const matchClass = req.originalUrl.match(/\/parse\/classes\/([a-zA-Z_]+)/); + if (matchClass.length > 1) { + const className = matchClass[1]; + if (!className.startsWith('Sandbox_')) { + changeUrl(req, className, `Sandbox_${className}`); + } + + return next(); + } + if ( + req.method === 'POST' && + req.originalUrl.startsWith('/parse/batch') && + req.body && + Array.isArray(req.body.requests) + ) { + req.body.requests = req.body.requests.map((elt) => ({ + ...elt, + path: transformPathForSandbox(elt.path) || elt.path, + })); + } + } + + return next(); +}; diff --git a/src/parse/cloud/setBeforeSave.js b/src/parse/cloud/setBeforeSave.js index d44a64c..22e51b5 100644 --- a/src/parse/cloud/setBeforeSave.js +++ b/src/parse/cloud/setBeforeSave.js @@ -38,7 +38,10 @@ module.exports = async (Parse) => { throw new Parse.Error(403, 'Please use OAuth to authenticate'); } - const schemaFile = `${__dirname}/../schema/classes/${schemaClass.className}.schema.json`; + const schemaFile = `${__dirname}/../schema/classes/${schemaClass.className.replace( + /^Sandbox_/, + '', + )}.schema.json`; const jsonObject = req.object.toJSON(); // remove extra fields added by Parse to validate the JSON delete jsonObject.createdAt; diff --git a/src/parse/index.js b/src/parse/index.js deleted file mode 100644 index 0489e9c..0000000 --- a/src/parse/index.js +++ /dev/null @@ -1,36 +0,0 @@ -const axios = require('axios'); -const { - PARSE_APP_ID, - PARSE_MASTER_KEY, - API_URL, - SANDBOX_URL, -} = require('../config'); - -class Parse { - static async signUp(username, password, isSandbox) { - const parseUrl = isSandbox - ? `${SANDBOX_URL}/parse-sandbox` - : `${API_URL}/parse`; - const parseApi = isSandbox ? `${PARSE_APP_ID}-sandbox` : PARSE_APP_ID; - - const response = await axios({ - method: 'post', - url: `${parseUrl}/users`, - data: { - username, - password, - }, - headers: { - 'Content-Type': 'application/json', - Accept: 'application/json', - 'X-Parse-Application-Id': parseApi, - 'X-Parse-Revocable-Session': 1, - 'X-Parse-Master-Key': PARSE_MASTER_KEY, - }, - }); - - return response.data; - } -} - -module.exports = Parse; diff --git a/src/parse/schema/getClasses.js b/src/parse/schema/getClasses.js index 6b863e4..8545144 100644 --- a/src/parse/schema/getClasses.js +++ b/src/parse/schema/getClasses.js @@ -31,9 +31,12 @@ const swaggerTypeToParseType = function (element) { return parseType; }; -const getClass = function (file) { +const getClass = function (file, isSandbox = false) { return new Promise((resolve, reject) => { - const className = path.parse(file).base.replace(/.schema.json$/, ''); + let className = path.parse(file).base.replace(/.schema.json$/, ''); + if (isSandbox) { + className = `Sandbox_${className}`; + } fs.readFile(file, 'utf8', (err, data) => { if (err) { reject(err); @@ -67,6 +70,7 @@ module.exports = function getClasses() { const promises = []; for (const file of files) { promises.push(getClass(file)); + promises.push(getClass(file, true)); } Promise.all(promises).then((result) => { classes = result; diff --git a/src/sandbox.js b/src/sandbox.js deleted file mode 100644 index ea69557..0000000 --- a/src/sandbox.js +++ /dev/null @@ -1,26 +0,0 @@ -const express = require('express'); -const cors = require('cors'); - -const logger = require('./logger'); -const { SANDBOX_PORT } = require('./config'); -const parseSandbox = require('./middleware/parseSandbox'); - -process.on('unhandledRejection', (err) => { - throw err; -}); - -const app = express(); - -const corsOptions = { - origin: '*', - optionsSuccessStatus: 200, // some legacy browsers (IE11, various SmartTVs) choke on 204 -}; - -app.use(cors(corsOptions)); - -// Serve the Parse API at /parse URL prefix -app.use('/parse-sandbox', parseSandbox()); - -app.listen(SANDBOX_PORT, () => { - logger.info(`connect sandbox running on port ${SANDBOX_PORT}.`); -});