mirror of
https://github.com/strapi/strapi.git
synced 2025-11-13 16:52:18 +00:00
Merge branch 'master' into patch-1
This commit is contained in:
commit
4b921d8778
@ -129,6 +129,7 @@ module.exports = {
|
|||||||
'/3.0.0-beta.x/guides/deployment',
|
'/3.0.0-beta.x/guides/deployment',
|
||||||
'/3.0.0-beta.x/guides/jwt-validation',
|
'/3.0.0-beta.x/guides/jwt-validation',
|
||||||
'/3.0.0-beta.x/guides/error-catching',
|
'/3.0.0-beta.x/guides/error-catching',
|
||||||
|
'/3.0.0-beta.x/guides/external-data',
|
||||||
'/3.0.0-beta.x/guides/slug',
|
'/3.0.0-beta.x/guides/slug',
|
||||||
'/3.0.0-beta.x/guides/webhooks',
|
'/3.0.0-beta.x/guides/webhooks',
|
||||||
],
|
],
|
||||||
|
|||||||
@ -67,7 +67,9 @@ module.exports = {
|
|||||||
entities = await strapi.services.restaurant.find(ctx.query);
|
entities = await strapi.services.restaurant.find(ctx.query);
|
||||||
}
|
}
|
||||||
|
|
||||||
return entities.map(entity => sanitizeEntity(entity, { model }));
|
return entities.map(entity =>
|
||||||
|
sanitizeEntity(entity, { model: strapi.models.restaurant })
|
||||||
|
);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
@ -90,7 +92,7 @@ module.exports = {
|
|||||||
|
|
||||||
async findOne(ctx) {
|
async findOne(ctx) {
|
||||||
const entity = await strapi.services.restaurant.findOne(ctx.params);
|
const entity = await strapi.services.restaurant.findOne(ctx.params);
|
||||||
return sanitizeEntity(entity, { model });
|
return sanitizeEntity(entity, { model: strapi.models.restaurant });
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
@ -142,7 +144,7 @@ module.exports = {
|
|||||||
} else {
|
} else {
|
||||||
entity = await strapi.services.restaurant.create(ctx.request.body);
|
entity = await strapi.services.restaurant.create(ctx.request.body);
|
||||||
}
|
}
|
||||||
return sanitizeEntity(entity, { model });
|
return sanitizeEntity(entity, { model: strapi.models.restaurant });
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
@ -177,7 +179,7 @@ module.exports = {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return sanitizeEntity(entity, { model });
|
return sanitizeEntity(entity, { model: strapi.models.restaurant });
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
@ -200,7 +202,7 @@ module.exports = {
|
|||||||
|
|
||||||
async delete(ctx) {
|
async delete(ctx) {
|
||||||
const entity = await strapi.services.restaurant.delete(ctx.params);
|
const entity = await strapi.services.restaurant.delete(ctx.params);
|
||||||
return sanitizeEntity(entity, { model });
|
return sanitizeEntity(entity, { model: strapi.models.restaurant });
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|||||||
@ -97,7 +97,7 @@ module.exports = {
|
|||||||
|
|
||||||
if (files) {
|
if (files) {
|
||||||
// automatically uploads the files based on the entry and the model
|
// automatically uploads the files based on the entry and the model
|
||||||
await this.uploadFiles(entry, files, { model });
|
await this.uploadFiles(entry, files, { model: strapi.models.restaurant });
|
||||||
return this.findOne({ id: entry.id });
|
return this.findOne({ id: entry.id });
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -125,7 +125,7 @@ module.exports = {
|
|||||||
|
|
||||||
if (files) {
|
if (files) {
|
||||||
// automatically uploads the files based on the entry and the model
|
// automatically uploads the files based on the entry and the model
|
||||||
await this.uploadFiles(entry, files, { model });
|
await this.uploadFiles(entry, files, { model: strapi.models.restaurant });
|
||||||
return this.findOne({ id: entry.id });
|
return this.findOne({ id: entry.id });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -89,7 +89,7 @@ Sort according to a specific field.
|
|||||||
- ASC: `GET /users?_sort=email:ASC`
|
- ASC: `GET /users?_sort=email:ASC`
|
||||||
- DESC: `GET /users?_sort=email:DESC`
|
- DESC: `GET /users?_sort=email:DESC`
|
||||||
|
|
||||||
#### Sorting on multiple fileds
|
#### Sorting on multiple fields
|
||||||
|
|
||||||
- `GET /users?_sort=email:asc,dateField:desc`
|
- `GET /users?_sort=email:asc,dateField:desc`
|
||||||
- `GET /users?_sort=email:DESC,username:ASC`
|
- `GET /users?_sort=email:DESC,username:ASC`
|
||||||
|
|||||||
96
docs/3.0.0-beta.x/guides/external-data.md
Normal file
96
docs/3.0.0-beta.x/guides/external-data.md
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
# Fetching external data
|
||||||
|
|
||||||
|
This guide explains how to fetch data from an external service to use it in your app.
|
||||||
|
|
||||||
|
In this example we will see how to daily fetch Docker pull count to store the result in your database.
|
||||||
|
|
||||||
|
## Content Type settings
|
||||||
|
|
||||||
|
First, we need to create a Content Type, in this example we will call it `hit` with a `date` and `count` attribute.
|
||||||
|
|
||||||
|
Your Content Type should look like this:
|
||||||
|
|
||||||
|
**Path —** `./api/hit/models/Hit.settings.json`
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"connection": "default",
|
||||||
|
"collectionName": "hits",
|
||||||
|
"info": {
|
||||||
|
"name": "hit",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"options": {
|
||||||
|
"increments": true,
|
||||||
|
"timestamps": true,
|
||||||
|
"comment": ""
|
||||||
|
},
|
||||||
|
"attributes": {
|
||||||
|
"count": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"date": {
|
||||||
|
"type": "date"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Fetch the data
|
||||||
|
|
||||||
|
Now we will create a function that will be usable everywhere in your strapi application.
|
||||||
|
|
||||||
|
**Path —** `./config/functions/docker.js`
|
||||||
|
|
||||||
|
```js
|
||||||
|
const axios = require('axios');
|
||||||
|
|
||||||
|
module.exports = async () => {
|
||||||
|
const { data } = await axios.get(
|
||||||
|
'https://hub.docker.com/v2/repositories/strapi/strapi/'
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log(data);
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
`data` contains all the data received from the Docker Hub API. What we want here is to add the `pull_count` value in your database.
|
||||||
|
|
||||||
|
## Create a `hit` entry
|
||||||
|
|
||||||
|
let's programmatically create the entry.
|
||||||
|
|
||||||
|
**Path —** `./config/functions/docker.js`
|
||||||
|
|
||||||
|
```js
|
||||||
|
const axios = require('axios');
|
||||||
|
|
||||||
|
module.exports = async () => {
|
||||||
|
const { data } = await axios.get(
|
||||||
|
'https://hub.docker.com/v2/repositories/strapi/strapi/'
|
||||||
|
);
|
||||||
|
|
||||||
|
await strapi.query('hit').create({
|
||||||
|
date: new Date(),
|
||||||
|
count: data.pull_count,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
With this code, everytime this function is called it will fetch the docker repo's data and insert the current `pull_count` with the corresponding date in your Strapi database.
|
||||||
|
|
||||||
|
## Call the function
|
||||||
|
|
||||||
|
Here is how to call the function in your application `strapi.config.functions.docker()`
|
||||||
|
|
||||||
|
So let's execute this function everyday at 2am. For this we will use a [CRON tasks](../concepts/configurations.md#cron-tasks).
|
||||||
|
|
||||||
|
**Path —** `./config/functions/cron.js`
|
||||||
|
|
||||||
|
```js
|
||||||
|
module.exports = {
|
||||||
|
'0 2 * * *': () => {
|
||||||
|
strapi.config.functions.docker();
|
||||||
|
},
|
||||||
|
};
|
||||||
|
```
|
||||||
@ -209,17 +209,42 @@ axios
|
|||||||
.post('http://localhost:1337/auth/reset-password', {
|
.post('http://localhost:1337/auth/reset-password', {
|
||||||
code: 'privateCode',
|
code: 'privateCode',
|
||||||
password: 'myNewPassword',
|
password: 'myNewPassword',
|
||||||
passwordConfirmation: 'myNewPassword'
|
passwordConfirmation: 'myNewPassword',
|
||||||
})
|
})
|
||||||
.then(response => {
|
.then(response => {
|
||||||
// Handle success.
|
// Handle success.
|
||||||
console.log('Your user\'s password has been changed.');
|
console.log("Your user's password has been changed.");
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
// Handle error.
|
// Handle error.
|
||||||
console.log('An error occurred:', error);
|
console.log('An error occurred:', error);
|
||||||
});
|
});
|
||||||
});
|
```
|
||||||
|
|
||||||
|
### Email validation
|
||||||
|
|
||||||
|
This action send an email to a user with the link to confirm the user.
|
||||||
|
|
||||||
|
#### Usage
|
||||||
|
|
||||||
|
- email is the user email.
|
||||||
|
|
||||||
|
```js
|
||||||
|
import axios from 'axios';
|
||||||
|
|
||||||
|
// Request API.
|
||||||
|
axios
|
||||||
|
.post(`http://localhost:1337/auth/send-email-confirmation`, {
|
||||||
|
email: 'user@strapi.io',
|
||||||
|
})
|
||||||
|
.then(response => {
|
||||||
|
// Handle success.
|
||||||
|
console.log('Your user received an email');
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
// Handle error.
|
||||||
|
console.err('An error occured:', err);
|
||||||
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
## User object in Strapi context
|
## User object in Strapi context
|
||||||
|
|||||||
@ -1,12 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import { shallow } from 'enzyme';
|
|
||||||
import { LoadingIndicatorPage } from 'strapi-helper-plugin';
|
|
||||||
|
|
||||||
import { App } from '../../App';
|
|
||||||
|
|
||||||
describe('<App />', () => {
|
|
||||||
it('should render the <AppLoader />', () => {
|
|
||||||
const renderedComponent = shallow(<App getDataSucceeded={jest.fn()} />);
|
|
||||||
expect(renderedComponent.find(LoadingIndicatorPage)).toHaveLength(1);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@ -44,7 +44,7 @@ async function askDbInfosAndTest(scope) {
|
|||||||
dependencies: clientDependencies({ scope, client }),
|
dependencies: clientDependencies({ scope, client }),
|
||||||
};
|
};
|
||||||
|
|
||||||
await testDatabaseConnection({
|
return testDatabaseConnection({
|
||||||
scope,
|
scope,
|
||||||
configuration,
|
configuration,
|
||||||
})
|
})
|
||||||
@ -67,6 +67,7 @@ async function askDbInfosAndTest(scope) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
.then(() => configuration)
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
if (retries < MAX_RETRIES - 1) {
|
if (retries < MAX_RETRIES - 1) {
|
||||||
console.log();
|
console.log();
|
||||||
@ -88,8 +89,6 @@ async function askDbInfosAndTest(scope) {
|
|||||||
`️⛔️ Could not connect to your database after ${MAX_RETRIES} tries. Try to check your database configuration an retry.`
|
`️⛔️ Could not connect to your database after ${MAX_RETRIES} tries. Try to check your database configuration an retry.`
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
return configuration;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return loop();
|
return loop();
|
||||||
|
|||||||
@ -3,24 +3,16 @@
|
|||||||
module.exports = ({ connection, env }) => {
|
module.exports = ({ connection, env }) => {
|
||||||
// Production/Staging Template
|
// Production/Staging Template
|
||||||
if (['production', 'staging'].includes(env)) {
|
if (['production', 'staging'].includes(env)) {
|
||||||
// All available settings (bookshelf and mongoose)
|
|
||||||
const settingsBase = {
|
const settingsBase = {
|
||||||
client: connection.settings.client,
|
client: connection.settings.client,
|
||||||
host: "${process.env.DATABASE_HOST || '127.0.0.1'}",
|
host: "${process.env.DATABASE_HOST || '127.0.0.1'}",
|
||||||
port: '${process.env.DATABASE_PORT || 27017}',
|
port: '${process.env.DATABASE_PORT || 27017}',
|
||||||
srv: '${process.env.DATABASE_SRV || false}',
|
|
||||||
database: "${process.env.DATABASE_NAME || 'strapi'}",
|
database: "${process.env.DATABASE_NAME || 'strapi'}",
|
||||||
username: "${process.env.DATABASE_USERNAME || ''}",
|
username: "${process.env.DATABASE_USERNAME || ''}",
|
||||||
password: "${process.env.DATABASE_PASSWORD || ''}",
|
password: "${process.env.DATABASE_PASSWORD || ''}",
|
||||||
ssl: '${process.env.DATABASE_SSL || false}',
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// All available options (bookshelf and mongoose)
|
const optionsBase = {};
|
||||||
const optionsBase = {
|
|
||||||
ssl: '${process.env.DATABASE_SSL || false}',
|
|
||||||
authenticationDatabase:
|
|
||||||
"${process.env.DATABASE_AUTHENTICATION_DATABASE || ''}",
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
defaultConnection: 'default',
|
defaultConnection: 'default',
|
||||||
|
|||||||
@ -6,7 +6,7 @@
|
|||||||
/* eslint-disable react/require-default-props */
|
/* eslint-disable react/require-default-props */
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { isEmpty, isObject, merge } from 'lodash';
|
import { isEmpty, merge } from 'lodash';
|
||||||
|
|
||||||
// Design
|
// Design
|
||||||
import InputAddonWithErrors from '../InputAddonWithErrors';
|
import InputAddonWithErrors from '../InputAddonWithErrors';
|
||||||
@ -22,7 +22,11 @@ import InputTextAreaWithErrors from '../InputTextAreaWithErrors';
|
|||||||
import InputTextWithErrors from '../InputTextWithErrors';
|
import InputTextWithErrors from '../InputTextWithErrors';
|
||||||
import InputToggleWithErrors from '../InputToggleWithErrors';
|
import InputToggleWithErrors from '../InputToggleWithErrors';
|
||||||
|
|
||||||
const DefaultInputError = ({ type }) => <div>Your input type: <b>{type}</b> does not exist</div>;
|
const DefaultInputError = ({ type }) => (
|
||||||
|
<div>
|
||||||
|
Your input type: <b>{type}</b> does not exist
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
const inputs = {
|
const inputs = {
|
||||||
addon: InputAddonWithErrors,
|
addon: InputAddonWithErrors,
|
||||||
@ -55,7 +59,7 @@ function InputsIndex(props) {
|
|||||||
inputValue = props.value || [];
|
inputValue = props.value || [];
|
||||||
break;
|
break;
|
||||||
case 'json':
|
case 'json':
|
||||||
inputValue = isObject(props.value) ? props.value : null;
|
inputValue = props.value || null;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
inputValue = props.value || '';
|
inputValue = props.value || '';
|
||||||
@ -78,10 +82,7 @@ InputsIndex.defaultProps = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
InputsIndex.propTypes = {
|
InputsIndex.propTypes = {
|
||||||
addon: PropTypes.oneOfType([
|
addon: PropTypes.oneOfType([PropTypes.bool, PropTypes.string]),
|
||||||
PropTypes.bool,
|
|
||||||
PropTypes.string,
|
|
||||||
]),
|
|
||||||
customInputs: PropTypes.object,
|
customInputs: PropTypes.object,
|
||||||
type: PropTypes.string.isRequired,
|
type: PropTypes.string.isRequired,
|
||||||
value: PropTypes.any,
|
value: PropTypes.any,
|
||||||
|
|||||||
@ -15,13 +15,12 @@ import 'codemirror/addon/selection/mark-selection';
|
|||||||
import 'codemirror/lib/codemirror.css';
|
import 'codemirror/lib/codemirror.css';
|
||||||
import 'codemirror/theme/3024-night.css';
|
import 'codemirror/theme/3024-night.css';
|
||||||
|
|
||||||
import { isEmpty, isObject, trimStart } from 'lodash';
|
import { isEmpty, trimStart } from 'lodash';
|
||||||
import jsonlint from './jsonlint';
|
import jsonlint from './jsonlint';
|
||||||
import Wrapper from './components';
|
import Wrapper from './components';
|
||||||
|
|
||||||
const WAIT = 600;
|
const WAIT = 600;
|
||||||
const stringify = JSON.stringify;
|
const stringify = JSON.stringify;
|
||||||
const parse = JSON.parse;
|
|
||||||
const DEFAULT_THEME = '3024-night';
|
const DEFAULT_THEME = '3024-night';
|
||||||
|
|
||||||
class InputJSON extends React.Component {
|
class InputJSON extends React.Component {
|
||||||
@ -65,16 +64,15 @@ class InputJSON extends React.Component {
|
|||||||
setInitValue = () => {
|
setInitValue = () => {
|
||||||
const { value } = this.props;
|
const { value } = this.props;
|
||||||
|
|
||||||
if (isObject(value) && value !== null) {
|
|
||||||
try {
|
try {
|
||||||
parse(stringify(value));
|
|
||||||
this.setState({ hasInitValue: true });
|
this.setState({ hasInitValue: true });
|
||||||
|
|
||||||
|
if (value === null) return this.codeMirror.setValue('');
|
||||||
|
|
||||||
return this.codeMirror.setValue(stringify(value, null, 2));
|
return this.codeMirror.setValue(stringify(value, null, 2));
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
return this.setState({ error: true });
|
return this.setState({ error: true });
|
||||||
}
|
}
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
setSize = () => this.codeMirror.setSize('100%', 'auto');
|
setSize = () => this.codeMirror.setSize('100%', 'auto');
|
||||||
@ -125,10 +123,8 @@ class InputJSON extends React.Component {
|
|||||||
const { name, onChange } = this.props;
|
const { name, onChange } = this.props;
|
||||||
let value = this.codeMirror.getValue();
|
let value = this.codeMirror.getValue();
|
||||||
|
|
||||||
try {
|
if (value === '') {
|
||||||
value = parse(value);
|
value = null;
|
||||||
} catch (err) {
|
|
||||||
// Silent
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the parent
|
// Update the parent
|
||||||
|
|||||||
@ -217,11 +217,7 @@ InputJSONWithErrors.propTypes = {
|
|||||||
resetProps: PropTypes.bool,
|
resetProps: PropTypes.bool,
|
||||||
tabIndex: PropTypes.string,
|
tabIndex: PropTypes.string,
|
||||||
validations: PropTypes.object,
|
validations: PropTypes.object,
|
||||||
value: PropTypes.oneOfType([
|
value: PropTypes.any,
|
||||||
PropTypes.array,
|
|
||||||
PropTypes.object,
|
|
||||||
PropTypes.bool,
|
|
||||||
]),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default InputJSONWithErrors;
|
export default InputJSONWithErrors;
|
||||||
|
|||||||
@ -19,7 +19,7 @@ export const cleanData = (retrievedData, ctLayout, groupLayouts) => {
|
|||||||
|
|
||||||
switch (attrType) {
|
switch (attrType) {
|
||||||
case 'json':
|
case 'json':
|
||||||
cleanedData = value;
|
cleanedData = JSON.parse(value);
|
||||||
break;
|
break;
|
||||||
case 'date':
|
case 'date':
|
||||||
cleanedData =
|
cleanedData =
|
||||||
|
|||||||
@ -1,12 +1,4 @@
|
|||||||
import {
|
import { get, isBoolean, isNaN } from 'lodash';
|
||||||
get,
|
|
||||||
isBoolean,
|
|
||||||
isNaN,
|
|
||||||
isNumber,
|
|
||||||
isNull,
|
|
||||||
isArray,
|
|
||||||
isObject,
|
|
||||||
} from 'lodash';
|
|
||||||
import * as yup from 'yup';
|
import * as yup from 'yup';
|
||||||
import { translatedErrors as errorsTrads } from 'strapi-helper-plugin';
|
import { translatedErrors as errorsTrads } from 'strapi-helper-plugin';
|
||||||
|
|
||||||
@ -99,20 +91,13 @@ const createYupSchemaAttribute = (type, validations) => {
|
|||||||
schema = yup
|
schema = yup
|
||||||
.mixed(errorsTrads.json)
|
.mixed(errorsTrads.json)
|
||||||
.test('isJSON', errorsTrads.json, value => {
|
.test('isJSON', errorsTrads.json, value => {
|
||||||
try {
|
if (value === undefined) {
|
||||||
if (
|
|
||||||
isObject(value) ||
|
|
||||||
isBoolean(value) ||
|
|
||||||
isNumber(value) ||
|
|
||||||
isArray(value) ||
|
|
||||||
isNaN(value) ||
|
|
||||||
isNull(value)
|
|
||||||
) {
|
|
||||||
JSON.parse(JSON.stringify(value));
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
try {
|
||||||
|
JSON.parse(value);
|
||||||
|
return true;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -358,12 +358,12 @@ const formatConnectionAggregator = function(fields, model, modelName) {
|
|||||||
|
|
||||||
if (opts._q) {
|
if (opts._q) {
|
||||||
// allow search param
|
// allow search param
|
||||||
return strapi.query(modelName).countSearch(opts);
|
return strapi.query(modelName, model.plugin).countSearch(opts);
|
||||||
}
|
}
|
||||||
return strapi.query(modelName).count(opts);
|
return strapi.query(modelName, model.plugin).count(opts);
|
||||||
},
|
},
|
||||||
totalCount(obj, options, context) {
|
totalCount(obj, options, context) {
|
||||||
return strapi.query(modelName).count({});
|
return strapi.query(modelName, model.plugin).count({});
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@ -285,6 +285,20 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"method": "POST",
|
||||||
|
"path": "/auth/send-email-confirmation",
|
||||||
|
"handler": "Auth.sendEmailConfirmation",
|
||||||
|
"config": {
|
||||||
|
"policies": [],
|
||||||
|
"prefix": "",
|
||||||
|
"description": "Send a confirmation email to user",
|
||||||
|
"tag": {
|
||||||
|
"plugin": "users-permissions",
|
||||||
|
"name": "User"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"method": "GET",
|
"method": "GET",
|
||||||
"path": "/users",
|
"path": "/users",
|
||||||
|
|||||||
@ -597,4 +597,80 @@ module.exports = {
|
|||||||
|
|
||||||
ctx.redirect(settings.email_confirmation_redirection || '/');
|
ctx.redirect(settings.email_confirmation_redirection || '/');
|
||||||
},
|
},
|
||||||
|
|
||||||
|
async sendEmailConfirmation(ctx) {
|
||||||
|
const pluginStore = await strapi.store({
|
||||||
|
environment: '',
|
||||||
|
type: 'plugin',
|
||||||
|
name: 'users-permissions',
|
||||||
|
});
|
||||||
|
|
||||||
|
const params = _.assign(ctx.request.body);
|
||||||
|
|
||||||
|
if (!params.email) {
|
||||||
|
return ctx.badRequest('missing.email');
|
||||||
|
}
|
||||||
|
|
||||||
|
const isEmail = emailRegExp.test(params.email);
|
||||||
|
|
||||||
|
if (isEmail) {
|
||||||
|
params.email = params.email.toLowerCase();
|
||||||
|
} else {
|
||||||
|
return ctx.badRequest('wrong.email');
|
||||||
|
}
|
||||||
|
|
||||||
|
const user = await strapi.query('user', 'users-permissions').findOne({
|
||||||
|
email: params.email
|
||||||
|
});
|
||||||
|
|
||||||
|
if (user.confirmed) {
|
||||||
|
return ctx.badRequest('already.confirmed');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (user.blocked) {
|
||||||
|
return ctx.badRequest('blocked.user');
|
||||||
|
}
|
||||||
|
|
||||||
|
const jwt = strapi.plugins['users-permissions'].services.jwt.issue(
|
||||||
|
_.pick(user.toJSON ? user.toJSON() : user, ['id'])
|
||||||
|
);
|
||||||
|
|
||||||
|
const settings = await pluginStore.get({ key: 'email' }).then(storeEmail => {
|
||||||
|
try {
|
||||||
|
return storeEmail['email_confirmation'].options;
|
||||||
|
} catch (err) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
settings.message = await strapi.plugins['users-permissions'].services.userspermissions.template(settings.message, {
|
||||||
|
URL: new URL('/auth/email-confirmation', strapi.config.url).toString(),
|
||||||
|
USER: _.omit(user.toJSON ? user.toJSON() : user, ['password', 'resetPasswordToken', 'role', 'provider']),
|
||||||
|
CODE: jwt
|
||||||
|
});
|
||||||
|
|
||||||
|
settings.object = await strapi.plugins['users-permissions'].services.userspermissions.template(settings.object, {
|
||||||
|
USER: _.omit(user.toJSON ? user.toJSON() : user, ['password', 'resetPasswordToken', 'role', 'provider']),
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
await strapi.plugins['email'].services.email.send({
|
||||||
|
to: (user.toJSON ? user.toJSON() : user).email,
|
||||||
|
from:
|
||||||
|
settings.from.email && settings.from.name
|
||||||
|
? `"${settings.from.name}" <${settings.from.email}>`
|
||||||
|
: undefined,
|
||||||
|
replyTo: settings.response_email,
|
||||||
|
subject: settings.object,
|
||||||
|
text: settings.message,
|
||||||
|
html: settings.message
|
||||||
|
});
|
||||||
|
ctx.send({
|
||||||
|
email: (user.toJSON ? user.toJSON() : user).email,
|
||||||
|
sent: true
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
return ctx.badRequest(null, err);
|
||||||
|
}
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@ -53,6 +53,47 @@
|
|||||||
"security": []
|
"security": []
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/auth/send-email-confirmation": {
|
||||||
|
"post": {
|
||||||
|
"security": [],
|
||||||
|
"externalDocs": {
|
||||||
|
"description": "Find out more in the strapi's documentation",
|
||||||
|
"url": "https://strapi.io/documentation/guides/authentication.html#usage"
|
||||||
|
},
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "Successfully sent email",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"email": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"sent": {
|
||||||
|
"type": "boolean"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"requestBody": {
|
||||||
|
"description": "",
|
||||||
|
"required": true,
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"required": ["email"],
|
||||||
|
"properties": {
|
||||||
|
"email": {
|
||||||
|
"type": "string",
|
||||||
|
"minLength": 6
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/users-permissions/search/{id}": {
|
"/users-permissions/search/{id}": {
|
||||||
"get": {
|
"get": {
|
||||||
"summary": "Retrieve a list of users by searching for their username or email",
|
"summary": "Retrieve a list of users by searching for their username or email",
|
||||||
|
|||||||
@ -389,7 +389,9 @@ module.exports = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Retrieve roles
|
// Retrieve roles
|
||||||
const roles = await strapi.query('role', 'users-permissions').find();
|
const roles = await strapi
|
||||||
|
.query('role', 'users-permissions')
|
||||||
|
.find({}, []);
|
||||||
|
|
||||||
// We have to know the difference to add or remove
|
// We have to know the difference to add or remove
|
||||||
// the permissions entries in the database.
|
// the permissions entries in the database.
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user