Merge branch 'feature/public-api' into feature/sketch-collections
This commit is contained in:
commit
846d2bb7db
19 changed files with 93 additions and 57 deletions
17
.github/config.yml
vendored
Normal file
17
.github/config.yml
vendored
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
# Configuration for welcome - https://github.com/behaviorbot/welcome
|
||||||
|
|
||||||
|
# Configuration for new-issue-welcome - https://github.com/behaviorbot/new-issue-welcome
|
||||||
|
|
||||||
|
# Comment to be posted to on first time issues
|
||||||
|
newIssueWelcomeComment: >
|
||||||
|
Welcome! 👋 Thanks for opening your first issue here! And to ensure the community is able to respond to your issue, be sure to follow the issue template if you haven't already.
|
||||||
|
|
||||||
|
# Configuration for new-pr-welcome - https://github.com/behaviorbot/new-pr-welcome
|
||||||
|
|
||||||
|
# Comment to be posted to on PRs from first time contributors in your repository
|
||||||
|
newPRWelcomeComment: >
|
||||||
|
🎉 Thanks for opening this pull request! Please check out our [contributing guidelines](https://github.com/processing/p5.js-web-editor/blob/master/CONTRIBUTING.md) if you haven't already.
|
||||||
|
|
||||||
|
# Configuration for first-pr-merge - https://github.com/behaviorbot/first-pr-merge
|
||||||
|
|
||||||
|
# Comment to be posted to on pull requests merged by a first time user
|
|
@ -46,4 +46,4 @@ deploy:
|
||||||
|
|
||||||
env:
|
env:
|
||||||
global:
|
global:
|
||||||
- APP_IMAGE_NAME=p5jswebeditor_app
|
- APP_IMAGE_NAME=p5js-web-editor_app
|
||||||
|
|
|
@ -131,12 +131,12 @@ class Nav extends React.PureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
handleAddFile() {
|
handleAddFile() {
|
||||||
this.props.newFile();
|
this.props.newFile(this.props.rootFile.id);
|
||||||
this.setDropdown('none');
|
this.setDropdown('none');
|
||||||
}
|
}
|
||||||
|
|
||||||
handleAddFolder() {
|
handleAddFolder() {
|
||||||
this.props.newFolder();
|
this.props.newFolder(this.props.rootFile.id);
|
||||||
this.setDropdown('none');
|
this.setDropdown('none');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -174,7 +174,7 @@ class Nav extends React.PureComponent {
|
||||||
|
|
||||||
handleDownload() {
|
handleDownload() {
|
||||||
this.props.autosaveProject();
|
this.props.autosaveProject();
|
||||||
this.props.exportProjectAsZip(this.props.project.id);
|
projectActions.exportProjectAsZip(this.props.project.id);
|
||||||
this.setDropdown('none');
|
this.setDropdown('none');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -719,7 +719,6 @@ Nav.propTypes = {
|
||||||
setToastText: PropTypes.func.isRequired,
|
setToastText: PropTypes.func.isRequired,
|
||||||
saveProject: PropTypes.func.isRequired,
|
saveProject: PropTypes.func.isRequired,
|
||||||
autosaveProject: PropTypes.func.isRequired,
|
autosaveProject: PropTypes.func.isRequired,
|
||||||
exportProjectAsZip: PropTypes.func.isRequired,
|
|
||||||
cloneProject: PropTypes.func.isRequired,
|
cloneProject: PropTypes.func.isRequired,
|
||||||
user: PropTypes.shape({
|
user: PropTypes.shape({
|
||||||
authenticated: PropTypes.bool.isRequired,
|
authenticated: PropTypes.bool.isRequired,
|
||||||
|
@ -750,7 +749,10 @@ Nav.propTypes = {
|
||||||
setAllAccessibleOutput: PropTypes.func.isRequired,
|
setAllAccessibleOutput: PropTypes.func.isRequired,
|
||||||
newFile: PropTypes.func.isRequired,
|
newFile: PropTypes.func.isRequired,
|
||||||
newFolder: PropTypes.func.isRequired,
|
newFolder: PropTypes.func.isRequired,
|
||||||
layout: PropTypes.oneOf(['dashboard', 'project'])
|
layout: PropTypes.oneOf(['dashboard', 'project']),
|
||||||
|
rootFile: PropTypes.shape({
|
||||||
|
id: PropTypes.string.isRequired
|
||||||
|
}).isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
Nav.defaultProps = {
|
Nav.defaultProps = {
|
||||||
|
@ -767,7 +769,8 @@ function mapStateToProps(state) {
|
||||||
return {
|
return {
|
||||||
project: state.project,
|
project: state.project,
|
||||||
user: state.user,
|
user: state.user,
|
||||||
unsavedChanges: state.ide.unsavedChanges
|
unsavedChanges: state.ide.unsavedChanges,
|
||||||
|
rootFile: state.files.filter(file => file.name === 'root')[0]
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -39,7 +39,12 @@ describe('Nav', () => {
|
||||||
},
|
},
|
||||||
startSketch: jest.fn(),
|
startSketch: jest.fn(),
|
||||||
stopSketch: jest.fn(),
|
stopSketch: jest.fn(),
|
||||||
setAllAccessibleOutput: jest.fn()
|
setAllAccessibleOutput: jest.fn(),
|
||||||
|
showToast: jest.fn(),
|
||||||
|
setToastText: jest.fn(),
|
||||||
|
rootFile: {
|
||||||
|
id: 'root-file'
|
||||||
|
}
|
||||||
};
|
};
|
||||||
const getWrapper = () => shallow(<NavComponent {...props} />);
|
const getWrapper = () => shallow(<NavComponent {...props} />);
|
||||||
|
|
||||||
|
|
|
@ -41,14 +41,7 @@ export function updateFileContent(id, content) {
|
||||||
export function createFile(formProps) {
|
export function createFile(formProps) {
|
||||||
return (dispatch, getState) => {
|
return (dispatch, getState) => {
|
||||||
const state = getState();
|
const state = getState();
|
||||||
const selectedFile = state.files.find(file => file.isSelectedFile);
|
const { parentId } = state.ide;
|
||||||
const rootFile = state.files.find(file => file.name === 'root');
|
|
||||||
let parentId;
|
|
||||||
if (selectedFile.fileType === 'folder') {
|
|
||||||
parentId = selectedFile.id;
|
|
||||||
} else {
|
|
||||||
parentId = rootFile.id;
|
|
||||||
}
|
|
||||||
if (state.project.id) {
|
if (state.project.id) {
|
||||||
const postParams = {
|
const postParams = {
|
||||||
name: createUniqueName(formProps.name, parentId, state.files),
|
name: createUniqueName(formProps.name, parentId, state.files),
|
||||||
|
@ -99,14 +92,7 @@ export function createFile(formProps) {
|
||||||
export function createFolder(formProps) {
|
export function createFolder(formProps) {
|
||||||
return (dispatch, getState) => {
|
return (dispatch, getState) => {
|
||||||
const state = getState();
|
const state = getState();
|
||||||
const selectedFile = state.files.find(file => file.isSelectedFile);
|
const { parentId } = state.ide;
|
||||||
const rootFile = state.files.find(file => file.name === 'root');
|
|
||||||
let parentId;
|
|
||||||
if (selectedFile.fileType === 'folder') {
|
|
||||||
parentId = selectedFile.id;
|
|
||||||
} else {
|
|
||||||
parentId = rootFile.id;
|
|
||||||
}
|
|
||||||
if (state.project.id) {
|
if (state.project.id) {
|
||||||
const postParams = {
|
const postParams = {
|
||||||
name: createUniqueName(formProps.name, parentId, state.files),
|
name: createUniqueName(formProps.name, parentId, state.files),
|
||||||
|
|
|
@ -62,9 +62,10 @@ export function resetSelectedFile(previousId) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function newFile() {
|
export function newFile(parentId) {
|
||||||
return {
|
return {
|
||||||
type: ActionTypes.SHOW_MODAL
|
type: ActionTypes.SHOW_MODAL,
|
||||||
|
parentId
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -122,9 +123,10 @@ export function closeProjectOptions() {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function newFolder() {
|
export function newFolder(parentId) {
|
||||||
return {
|
return {
|
||||||
type: ActionTypes.SHOW_NEW_FOLDER_MODAL
|
type: ActionTypes.SHOW_NEW_FOLDER_MODAL,
|
||||||
|
parentId
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -82,7 +82,7 @@ export function dropzoneSendingCallback(file, xhr, formData) {
|
||||||
Object.keys(file.postData).forEach((key) => {
|
Object.keys(file.postData).forEach((key) => {
|
||||||
formData.append(key, file.postData[key]);
|
formData.append(key, file.postData[key]);
|
||||||
});
|
});
|
||||||
formData.append('Content-type', '');
|
formData.append('Content-type', file.type);
|
||||||
formData.append('Content-length', '');
|
formData.append('Content-length', '');
|
||||||
formData.append('acl', 'public-read');
|
formData.append('acl', 'public-read');
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,19 +56,21 @@ class AssetList extends React.Component {
|
||||||
<table className="asset-table">
|
<table className="asset-table">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Name</th>
|
<th scope="col">Name</th>
|
||||||
<th>Size</th>
|
<th scope="col">Size</th>
|
||||||
<th>View</th>
|
<th scope="col">Sketch</th>
|
||||||
<th>Sketch</th>
|
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{assetList.map(asset =>
|
{assetList.map(asset =>
|
||||||
(
|
(
|
||||||
<tr className="asset-table__row" key={asset.key}>
|
<tr className="asset-table__row" key={asset.key}>
|
||||||
<td>{asset.name}</td>
|
<th scope="row">
|
||||||
|
<Link to={asset.url} target="_blank">
|
||||||
|
{asset.name}
|
||||||
|
</Link>
|
||||||
|
</th>
|
||||||
<td>{prettyBytes(asset.size)}</td>
|
<td>{prettyBytes(asset.size)}</td>
|
||||||
<td><Link to={asset.url} target="_blank">View</Link></td>
|
|
||||||
<td><Link to={`/${username}/sketches/${asset.sketchId}`}>{asset.sketchName}</Link></td>
|
<td><Link to={`/${username}/sketches/${asset.sketchId}`}>{asset.sketchName}</Link></td>
|
||||||
</tr>
|
</tr>
|
||||||
))}
|
))}
|
||||||
|
|
|
@ -108,7 +108,15 @@ class Editor extends React.Component {
|
||||||
delete this._cm.options.lint.options.errors;
|
delete this._cm.options.lint.options.errors;
|
||||||
|
|
||||||
this._cm.setOption('extraKeys', {
|
this._cm.setOption('extraKeys', {
|
||||||
Tab: cm => cm.replaceSelection(' '.repeat(INDENTATION_AMOUNT)),
|
Tab: (cm) => {
|
||||||
|
// might need to specify and indent more?
|
||||||
|
const selection = cm.doc.getSelection();
|
||||||
|
if (selection.length > 0) {
|
||||||
|
cm.execCommand('indentMore');
|
||||||
|
} else {
|
||||||
|
cm.replaceSelection(' '.repeat(INDENTATION_AMOUNT));
|
||||||
|
}
|
||||||
|
},
|
||||||
[`${metaKey}-Enter`]: () => null,
|
[`${metaKey}-Enter`]: () => null,
|
||||||
[`Shift-${metaKey}-Enter`]: () => null,
|
[`Shift-${metaKey}-Enter`]: () => null,
|
||||||
[`${metaKey}-F`]: 'findPersistent',
|
[`${metaKey}-F`]: 'findPersistent',
|
||||||
|
@ -126,7 +134,7 @@ class Editor extends React.Component {
|
||||||
this.props.clearConsole();
|
this.props.clearConsole();
|
||||||
this.props.startRefreshSketch();
|
this.props.startRefreshSketch();
|
||||||
}
|
}
|
||||||
}, 400));
|
}, 1000));
|
||||||
|
|
||||||
this._cm.on('keyup', () => {
|
this._cm.on('keyup', () => {
|
||||||
const temp = `line ${parseInt((this._cm.getCursor().line) + 1, 10)}`;
|
const temp = `line ${parseInt((this._cm.getCursor().line) + 1, 10)}`;
|
||||||
|
|
|
@ -188,7 +188,7 @@ export class FileNode extends React.Component {
|
||||||
<button
|
<button
|
||||||
aria-label="add file"
|
aria-label="add file"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
this.props.newFile();
|
this.props.newFile(this.props.id);
|
||||||
setTimeout(() => this.hideFileOptions(), 0);
|
setTimeout(() => this.hideFileOptions(), 0);
|
||||||
}}
|
}}
|
||||||
onBlur={this.onBlurComponent}
|
onBlur={this.onBlurComponent}
|
||||||
|
@ -208,7 +208,7 @@ export class FileNode extends React.Component {
|
||||||
<button
|
<button
|
||||||
aria-label="add folder"
|
aria-label="add folder"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
this.props.newFolder();
|
this.props.newFolder(this.props.id);
|
||||||
setTimeout(() => this.hideFileOptions(), 0);
|
setTimeout(() => this.hideFileOptions(), 0);
|
||||||
}}
|
}}
|
||||||
onBlur={this.onBlurComponent}
|
onBlur={this.onBlurComponent}
|
||||||
|
|
|
@ -5,6 +5,7 @@ import classNames from 'classnames';
|
||||||
import InlineSVG from 'react-inlinesvg';
|
import InlineSVG from 'react-inlinesvg';
|
||||||
import NewFileForm from './NewFileForm';
|
import NewFileForm from './NewFileForm';
|
||||||
import FileUploader from './FileUploader';
|
import FileUploader from './FileUploader';
|
||||||
|
import { CREATE_FILE_REGEX } from '../../../../server/utils/fileUtils';
|
||||||
|
|
||||||
const exitUrl = require('../../../images/exit.svg');
|
const exitUrl = require('../../../images/exit.svg');
|
||||||
|
|
||||||
|
@ -71,8 +72,8 @@ function validate(formProps) {
|
||||||
|
|
||||||
if (!formProps.name) {
|
if (!formProps.name) {
|
||||||
errors.name = 'Please enter a name';
|
errors.name = 'Please enter a name';
|
||||||
} else if (!formProps.name.match(/(.+\.js$|.+\.css$|.+\.json$|.+\.txt$|.+\.csv$|.+\.tsv$)/i)) {
|
} else if (!formProps.name.match(CREATE_FILE_REGEX)) {
|
||||||
errors.name = 'File must be of type JavaScript, CSS, JSON, TXT, CSV, or TSV.';
|
errors.name = 'Invalid file type. Valid extensions are .js, .css, .json, .txt, .csv, .tsv, .frag, and .vert.';
|
||||||
}
|
}
|
||||||
|
|
||||||
return errors;
|
return errors;
|
||||||
|
|
|
@ -66,6 +66,7 @@ class Sidebar extends React.Component {
|
||||||
'sidebar--project-options': this.props.projectOptionsVisible,
|
'sidebar--project-options': this.props.projectOptionsVisible,
|
||||||
'sidebar--cant-edit': !canEditProject
|
'sidebar--cant-edit': !canEditProject
|
||||||
});
|
});
|
||||||
|
const rootFile = this.props.files.filter(file => file.name === 'root')[0];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<nav className={sidebarClass} title="file-navigation" >
|
<nav className={sidebarClass} title="file-navigation" >
|
||||||
|
@ -90,7 +91,7 @@ class Sidebar extends React.Component {
|
||||||
<button
|
<button
|
||||||
aria-label="add folder"
|
aria-label="add folder"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
this.props.newFolder();
|
this.props.newFolder(rootFile.id);
|
||||||
setTimeout(this.props.closeProjectOptions, 0);
|
setTimeout(this.props.closeProjectOptions, 0);
|
||||||
}}
|
}}
|
||||||
onBlur={this.onBlurComponent}
|
onBlur={this.onBlurComponent}
|
||||||
|
@ -103,7 +104,7 @@ class Sidebar extends React.Component {
|
||||||
<button
|
<button
|
||||||
aria-label="add file"
|
aria-label="add file"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
this.props.newFile();
|
this.props.newFile(rootFile.id);
|
||||||
setTimeout(this.props.closeProjectOptions, 0);
|
setTimeout(this.props.closeProjectOptions, 0);
|
||||||
}}
|
}}
|
||||||
onBlur={this.onBlurComponent}
|
onBlur={this.onBlurComponent}
|
||||||
|
@ -116,7 +117,7 @@ class Sidebar extends React.Component {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<ConnectedFileNode
|
<ConnectedFileNode
|
||||||
id={this.props.files.filter(file => file.name === 'root')[0].id}
|
id={rootFile.id}
|
||||||
canEdit={canEditProject}
|
canEdit={canEditProject}
|
||||||
/>
|
/>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
|
@ -124,7 +124,7 @@ class Toolbar extends React.Component {
|
||||||
</a>
|
</a>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
maxLength="256"
|
maxLength="128"
|
||||||
className="toolbar__project-name-input"
|
className="toolbar__project-name-input"
|
||||||
value={this.props.project.name}
|
value={this.props.project.name}
|
||||||
onChange={this.handleProjectNameChange}
|
onChange={this.handleProjectNameChange}
|
||||||
|
|
|
@ -23,6 +23,7 @@ const initialState = {
|
||||||
previousPath: '/',
|
previousPath: '/',
|
||||||
errorType: undefined,
|
errorType: undefined,
|
||||||
runtimeErrorWarningVisible: true,
|
runtimeErrorWarningVisible: true,
|
||||||
|
parentId: undefined
|
||||||
};
|
};
|
||||||
|
|
||||||
const ide = (state = initialState, action) => {
|
const ide = (state = initialState, action) => {
|
||||||
|
@ -38,7 +39,7 @@ const ide = (state = initialState, action) => {
|
||||||
case ActionTypes.CONSOLE_EVENT:
|
case ActionTypes.CONSOLE_EVENT:
|
||||||
return Object.assign({}, state, { consoleEvent: action.event });
|
return Object.assign({}, state, { consoleEvent: action.event });
|
||||||
case ActionTypes.SHOW_MODAL:
|
case ActionTypes.SHOW_MODAL:
|
||||||
return Object.assign({}, state, { modalIsVisible: true });
|
return Object.assign({}, state, { modalIsVisible: true, parentId: action.parentId });
|
||||||
case ActionTypes.HIDE_MODAL:
|
case ActionTypes.HIDE_MODAL:
|
||||||
return Object.assign({}, state, { modalIsVisible: false });
|
return Object.assign({}, state, { modalIsVisible: false });
|
||||||
case ActionTypes.COLLAPSE_SIDEBAR:
|
case ActionTypes.COLLAPSE_SIDEBAR:
|
||||||
|
@ -60,7 +61,7 @@ const ide = (state = initialState, action) => {
|
||||||
case ActionTypes.CLOSE_PROJECT_OPTIONS:
|
case ActionTypes.CLOSE_PROJECT_OPTIONS:
|
||||||
return Object.assign({}, state, { projectOptionsVisible: false });
|
return Object.assign({}, state, { projectOptionsVisible: false });
|
||||||
case ActionTypes.SHOW_NEW_FOLDER_MODAL:
|
case ActionTypes.SHOW_NEW_FOLDER_MODAL:
|
||||||
return Object.assign({}, state, { newFolderModalVisible: true });
|
return Object.assign({}, state, { newFolderModalVisible: true, parentId: action.parentId });
|
||||||
case ActionTypes.CLOSE_NEW_FOLDER_MODAL:
|
case ActionTypes.CLOSE_NEW_FOLDER_MODAL:
|
||||||
return Object.assign({}, state, { newFolderModalVisible: false });
|
return Object.assign({}, state, { newFolderModalVisible: false });
|
||||||
case ActionTypes.SHOW_SHARE_MODAL:
|
case ActionTypes.SHOW_SHARE_MODAL:
|
||||||
|
|
|
@ -21,7 +21,11 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
& th {
|
.asset-table__row>th:nth-child(1) {
|
||||||
|
padding-left: #{12 / $base-font-size}rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
& thead th {
|
||||||
padding-top: #{10 / $base-font-size}rem;
|
padding-top: #{10 / $base-font-size}rem;
|
||||||
padding-bottom: #{15 / $base-font-size}rem;
|
padding-bottom: #{15 / $base-font-size}rem;
|
||||||
height: #{32 / $base-font-size}rem;
|
height: #{32 / $base-font-size}rem;
|
||||||
|
@ -32,6 +36,10 @@
|
||||||
background-color: getThemifyVariable('background-color');
|
background-color: getThemifyVariable('background-color');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
& th {
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.asset-table__row {
|
.asset-table__row {
|
||||||
|
|
|
@ -44,7 +44,9 @@ export function validateSettings(formProps) {
|
||||||
if (formProps.currentPassword && !formProps.newPassword) {
|
if (formProps.currentPassword && !formProps.newPassword) {
|
||||||
errors.newPassword = 'Please enter a new password or leave the current password empty.';
|
errors.newPassword = 'Please enter a new password or leave the current password empty.';
|
||||||
}
|
}
|
||||||
|
if (formProps.newPassword && formProps.newPassword.length < 6) {
|
||||||
|
errors.newPassword = 'Password must be at least 6 characters';
|
||||||
|
}
|
||||||
return errors;
|
return errors;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -67,11 +69,9 @@ export function validateSignup(formProps) {
|
||||||
if (!formProps.password) {
|
if (!formProps.password) {
|
||||||
errors.password = 'Please enter a password';
|
errors.password = 'Please enter a password';
|
||||||
}
|
}
|
||||||
if (formProps.password) {
|
if (formProps.password && formProps.password.length < 6) {
|
||||||
if (formProps.password.length < 6) {
|
|
||||||
errors.password = 'Password must be at least 6 characters';
|
errors.password = 'Password must be at least 6 characters';
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if (!formProps.confirmPassword) {
|
if (!formProps.confirmPassword) {
|
||||||
errors.confirmPassword = 'Please enter a password confirmation';
|
errors.confirmPassword = 'Please enter a password confirmation';
|
||||||
}
|
}
|
||||||
|
|
|
@ -130,7 +130,7 @@
|
||||||
"jest-express": "^1.10.1",
|
"jest-express": "^1.10.1",
|
||||||
"js-beautify": "^1.8.9",
|
"js-beautify": "^1.8.9",
|
||||||
"jsdom": "^9.8.3",
|
"jsdom": "^9.8.3",
|
||||||
"jshint": "^2.10.1",
|
"jshint": "^2.10.2",
|
||||||
"lodash": "^4.17.15",
|
"lodash": "^4.17.15",
|
||||||
"loop-protect": "github:catarak/loop-protect",
|
"loop-protect": "github:catarak/loop-protect",
|
||||||
"mjml": "^3.3.2",
|
"mjml": "^3.3.2",
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
export const fileExtensionsArray = ['gif', 'jpg', 'jpeg', 'png', 'bmp', 'wav', 'flac', 'ogg',
|
export const fileExtensionsArray = ['gif', 'jpg', 'jpeg', 'png', 'bmp', 'wav', 'flac', 'ogg',
|
||||||
'oga', 'mp4', 'm4p', 'mp3', 'm4a', 'aiff', 'aif', 'm4v', 'aac', 'webm', 'mpg', 'mp2',
|
'oga', 'mp4', 'm4p', 'mp3', 'm4a', 'aiff', 'aif', 'm4v', 'aac', 'webm', 'mpg', 'mp2',
|
||||||
'mpeg', 'mpe', 'mpv', 'js', 'jsx', 'html', 'htm', 'css', 'json', 'csv', 'obj', 'svg',
|
'mpeg', 'mpe', 'mpv', 'js', 'jsx', 'html', 'htm', 'css', 'json', 'csv', 'obj', 'svg',
|
||||||
'otf', 'ttf', 'txt', 'mov', 'vert', 'frag'];
|
'otf', 'ttf', 'txt', 'mov', 'vert', 'frag', 'bin'];
|
||||||
|
|
||||||
export const mimeTypes = `image/*,audio/*,text/javascript,text/html,text/css,
|
export const mimeTypes = `image/*,audio/*,text/javascript,text/html,text/css,
|
||||||
application/json,application/x-font-ttf,application/x-font-truetype,text/plain,
|
application/json,application/x-font-ttf,application/x-font-truetype,text/plain,
|
||||||
|
@ -22,6 +22,8 @@ export const STRING_REGEX = /(['"])((\\\1|.)*?)\1/gm;
|
||||||
// these are files that have to be linked to with a blob url
|
// these are files that have to be linked to with a blob url
|
||||||
export const PLAINTEXT_FILE_REGEX = /.+\.(json|txt|csv|vert|frag|tsv)$/i;
|
export const PLAINTEXT_FILE_REGEX = /.+\.(json|txt|csv|vert|frag|tsv)$/i;
|
||||||
// these are files that users would want to edit as text (maybe svg should be here?)
|
// these are files that users would want to edit as text (maybe svg should be here?)
|
||||||
export const TEXT_FILE_REGEX = /.+\.(json|txt|csv|vert|frag|js|css|html|htm|jsx)$/i;
|
export const TEXT_FILE_REGEX = /.+\.(json|txt|csv|tsv|vert|frag|js|css|html|htm|jsx)$/i;
|
||||||
export const NOT_EXTERNAL_LINK_REGEX = /^(?!(http:\/\/|https:\/\/))/;
|
export const NOT_EXTERNAL_LINK_REGEX = /^(?!(http:\/\/|https:\/\/))/;
|
||||||
export const EXTERNAL_LINK_REGEX = /^(http:\/\/|https:\/\/)/;
|
export const EXTERNAL_LINK_REGEX = /^(http:\/\/|https:\/\/)/;
|
||||||
|
|
||||||
|
export const CREATE_FILE_REGEX = /.+\.(json|txt|csv|tsv|js|css|frag|vert)$/i;
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
header does not match `type`
|
header does not match `type`
|
||||||
*/
|
*/
|
||||||
const requestsOfType = type => (req, res, next) => {
|
const requestsOfType = type => (req, res, next) => {
|
||||||
const hasContentType = req.get('content-type') !== null;
|
const hasContentType = req.get('content-type') !== undefined && req.get('content-type') !== null;
|
||||||
const isCorrectType = req.is(type) === null || req.is(type) === type;
|
const isCorrectType = req.is(type) === null || req.is(type) === type;
|
||||||
|
|
||||||
if (hasContentType && !isCorrectType) {
|
if (hasContentType && !isCorrectType) {
|
||||||
|
|
Loading…
Reference in a new issue