mirror of
https://github.com/plankanban/planka.git
synced 2025-07-18 20:59:44 +02:00
Add dropzone for attachment, paste attachment from clipboard
This commit is contained in:
parent
693602698b
commit
d264382fda
24 changed files with 576 additions and 253 deletions
23
client/package-lock.json
generated
23
client/package-lock.json
generated
|
@ -2198,6 +2198,11 @@
|
|||
"resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz",
|
||||
"integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg=="
|
||||
},
|
||||
"attr-accept": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/attr-accept/-/attr-accept-2.1.0.tgz",
|
||||
"integrity": "sha512-sLzVM3zCCmmDtDNhI0i96k6PUztkotSOXqE4kDGQt/6iDi5M+H0srjeF+QC6jN581l4X/Zq3Zu/tgcErEssavg=="
|
||||
},
|
||||
"autoprefixer": {
|
||||
"version": "9.7.5",
|
||||
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.7.5.tgz",
|
||||
|
@ -5566,6 +5571,14 @@
|
|||
"schema-utils": "^2.5.0"
|
||||
}
|
||||
},
|
||||
"file-selector": {
|
||||
"version": "0.1.12",
|
||||
"resolved": "https://registry.npmjs.org/file-selector/-/file-selector-0.1.12.tgz",
|
||||
"integrity": "sha512-Kx7RTzxyQipHuiqyZGf+Nz4vY9R1XGxuQl/hLoJwq+J4avk/9wxxgZyHKtbyIPJmbD4A66DWGYfyykWNpcYutQ==",
|
||||
"requires": {
|
||||
"tslib": "^1.9.0"
|
||||
}
|
||||
},
|
||||
"file-uri-to-path": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
|
||||
|
@ -11207,6 +11220,16 @@
|
|||
"scheduler": "^0.19.1"
|
||||
}
|
||||
},
|
||||
"react-dropzone": {
|
||||
"version": "11.0.1",
|
||||
"resolved": "https://registry.npmjs.org/react-dropzone/-/react-dropzone-11.0.1.tgz",
|
||||
"integrity": "sha512-x/6wqRHaR8jsrNiu/boVMIPYuoxb83Vyfv77hO7/3ZRn8Pr+KH5onsCsB8MLBa3zdJl410C5FXPUINbu16XIzw==",
|
||||
"requires": {
|
||||
"attr-accept": "^2.0.0",
|
||||
"file-selector": "^0.1.12",
|
||||
"prop-types": "^15.7.2"
|
||||
}
|
||||
},
|
||||
"react-error-overlay": {
|
||||
"version": "6.0.7",
|
||||
"resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.7.tgz",
|
||||
|
|
|
@ -60,6 +60,7 @@
|
|||
"react-beautiful-dnd": "^13.0.0",
|
||||
"react-datepicker": "^2.14.1",
|
||||
"react-dom": "^16.13.1",
|
||||
"react-dropzone": "^11.0.1",
|
||||
"react-i18next": "^11.3.4",
|
||||
"react-input-mask": "^2.0.4",
|
||||
"react-markdown": "^4.3.1",
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 103 KiB |
BIN
client/public/logo192.png
Normal file → Executable file
BIN
client/public/logo192.png
Normal file → Executable file
Binary file not shown.
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 2.6 KiB |
BIN
client/public/logo512.png
Normal file → Executable file
BIN
client/public/logo512.png
Normal file → Executable file
Binary file not shown.
Before Width: | Height: | Size: 5.4 KiB After Width: | Height: | Size: 5.2 KiB |
23
client/src/components/CardModal/AddAttachment.jsx
Normal file
23
client/src/components/CardModal/AddAttachment.jsx
Normal file
|
@ -0,0 +1,23 @@
|
|||
import React, { useCallback } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { FilePicker } from '../../lib/custom-ui';
|
||||
|
||||
const AddAttachment = React.memo(({ children, onCreate }) => {
|
||||
const handleFileSelect = useCallback(
|
||||
(file) => {
|
||||
onCreate({
|
||||
file,
|
||||
});
|
||||
},
|
||||
[onCreate],
|
||||
);
|
||||
|
||||
return <FilePicker onSelect={handleFileSelect}>{children}</FilePicker>;
|
||||
});
|
||||
|
||||
AddAttachment.propTypes = {
|
||||
children: PropTypes.element.isRequired,
|
||||
onCreate: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export default AddAttachment;
|
|
@ -0,0 +1,108 @@
|
|||
import React, { useCallback, useEffect } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { useDropzone } from 'react-dropzone';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { closePopup } from '../../../lib/popup';
|
||||
|
||||
import { useModal } from '../../../hooks';
|
||||
import AddTextFileModal from './AddTextFileModal';
|
||||
|
||||
import styles from './AddAttachmentZone.module.css';
|
||||
|
||||
const AddAttachmentZone = React.memo(({ children, onCreate }) => {
|
||||
const [t] = useTranslation();
|
||||
const [modal, openModal, handleModalClose] = useModal();
|
||||
|
||||
const submit = useCallback(
|
||||
(file) => {
|
||||
onCreate({
|
||||
file,
|
||||
});
|
||||
},
|
||||
[onCreate],
|
||||
);
|
||||
|
||||
const handleDropAccepted = useCallback(
|
||||
(files) => {
|
||||
submit(files[0]);
|
||||
},
|
||||
[submit],
|
||||
);
|
||||
|
||||
const { getRootProps, getInputProps, isDragActive } = useDropzone({
|
||||
multiple: false,
|
||||
noClick: true,
|
||||
noKeyboard: true,
|
||||
onDropAccepted: handleDropAccepted,
|
||||
});
|
||||
|
||||
const handleFileCreate = useCallback(
|
||||
(file) => {
|
||||
submit(file);
|
||||
},
|
||||
[submit],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const handlePaste = (event) => {
|
||||
const item = event.clipboardData && event.clipboardData.items[0];
|
||||
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (item.kind === 'file') {
|
||||
submit(item.getAsFile());
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
['input', 'textarea'].includes(event.target.tagName.toLowerCase()) &&
|
||||
event.target === document.activeElement
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
closePopup();
|
||||
event.preventDefault();
|
||||
|
||||
item.getAsString((content) => {
|
||||
openModal({
|
||||
content,
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
window.addEventListener('paste', handlePaste);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('paste', handlePaste);
|
||||
};
|
||||
}, [openModal, submit]);
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* eslint-disable-next-line react/jsx-props-no-spreading */}
|
||||
<div {...getRootProps()}>
|
||||
{isDragActive && <div className={styles.dropzone}>{t('common.dropFileToUpload')}</div>}
|
||||
{children}
|
||||
</div>
|
||||
{modal && (
|
||||
<AddTextFileModal
|
||||
content={modal.content}
|
||||
onCreate={handleFileCreate}
|
||||
onClose={handleModalClose}
|
||||
/>
|
||||
)}
|
||||
{/* eslint-disable-next-line react/jsx-props-no-spreading */}
|
||||
<input {...getInputProps()} />
|
||||
</>
|
||||
);
|
||||
});
|
||||
|
||||
AddAttachmentZone.propTypes = {
|
||||
children: PropTypes.element.isRequired,
|
||||
onCreate: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export default AddAttachmentZone;
|
|
@ -0,0 +1,13 @@
|
|||
.dropzone {
|
||||
background: white;
|
||||
font-size: 20px;
|
||||
font-weight: 700;
|
||||
height: 100%;
|
||||
line-height: 30px;
|
||||
opacity: 0.7;
|
||||
padding: 200px 50px;
|
||||
position: absolute;
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
z-index: 1;
|
||||
}
|
|
@ -0,0 +1,83 @@
|
|||
import React, { useCallback, useEffect, useRef } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Button, Form, Header, Modal } from 'semantic-ui-react';
|
||||
import { Input } from '../../../lib/custom-ui';
|
||||
|
||||
import { useForm } from '../../../hooks';
|
||||
|
||||
import styles from './AddTextFileModal.module.css';
|
||||
|
||||
const AddTextFileModal = React.memo(({ content, onCreate, onClose }) => {
|
||||
const [t] = useTranslation();
|
||||
|
||||
const [data, handleFieldChange] = useForm(() => ({
|
||||
name: '',
|
||||
}));
|
||||
|
||||
const nameField = useRef(null);
|
||||
|
||||
const handleSubmit = useCallback(() => {
|
||||
const cleanData = {
|
||||
...data,
|
||||
name: data.name.trim(),
|
||||
};
|
||||
|
||||
if (!cleanData.name) {
|
||||
nameField.current.select();
|
||||
return;
|
||||
}
|
||||
|
||||
const file = new File([content], `${cleanData.name}.txt`, {
|
||||
type: 'plain/text',
|
||||
});
|
||||
|
||||
onCreate(file);
|
||||
onClose();
|
||||
}, [content, onCreate, onClose, data]);
|
||||
|
||||
useEffect(() => {
|
||||
nameField.current.select();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Modal open basic centered closeIcon size="tiny" onClose={onClose}>
|
||||
<Modal.Content>
|
||||
<Header inverted size="huge">
|
||||
{t('common.createTextFile', {
|
||||
context: 'title',
|
||||
})}
|
||||
</Header>
|
||||
<p>{t('common.enterFilename')}</p>
|
||||
<Form onSubmit={handleSubmit}>
|
||||
<Input
|
||||
fluid
|
||||
inverted
|
||||
ref={nameField}
|
||||
name="name"
|
||||
value={data.name}
|
||||
label=".txt"
|
||||
labelPosition="right"
|
||||
className={styles.field}
|
||||
onChange={handleFieldChange}
|
||||
/>
|
||||
<Button
|
||||
inverted
|
||||
color="green"
|
||||
icon="checkmark"
|
||||
content={t('action.createFile')}
|
||||
floated="right"
|
||||
/>
|
||||
</Form>
|
||||
</Modal.Content>
|
||||
</Modal>
|
||||
);
|
||||
});
|
||||
|
||||
AddTextFileModal.propTypes = {
|
||||
content: PropTypes.string.isRequired,
|
||||
onCreate: PropTypes.func.isRequired,
|
||||
onClose: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export default AddTextFileModal;
|
|
@ -0,0 +1,3 @@
|
|||
.field {
|
||||
margin-bottom: 20px;
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
import AddAttachmentZone from './AddAttachmentZone';
|
||||
|
||||
export default AddAttachmentZone;
|
|
@ -38,8 +38,11 @@
|
|||
font-weight: 700;
|
||||
height: 100%;
|
||||
line-height: 80px;
|
||||
overflow: hidden;
|
||||
padding: 0 20px 0 20px;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
text-overflow: ellipsis;
|
||||
text-transform: uppercase;
|
||||
width: 100%;
|
||||
}
|
||||
|
|
|
@ -3,12 +3,14 @@ import PropTypes from 'prop-types';
|
|||
import classNames from 'classnames';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Button, Grid, Icon, Modal } from 'semantic-ui-react';
|
||||
import { FilePicker, Markdown } from '../../lib/custom-ui';
|
||||
import { Markdown } from '../../lib/custom-ui';
|
||||
|
||||
import NameField from './NameField';
|
||||
import EditDescription from './EditDescription';
|
||||
import Tasks from './Tasks';
|
||||
import Attachments from './Attachments';
|
||||
import AddAttachment from './AddAttachment';
|
||||
import AddAttachmentZone from './AddAttachmentZone';
|
||||
import Actions from './Actions';
|
||||
import User from '../User';
|
||||
import Label from '../Label';
|
||||
|
@ -107,15 +109,6 @@ const CardModal = React.memo(
|
|||
[onUpdate],
|
||||
);
|
||||
|
||||
const handleAttachmentFileSelect = useCallback(
|
||||
(file) => {
|
||||
onAttachmentCreate({
|
||||
file,
|
||||
});
|
||||
},
|
||||
[onAttachmentCreate],
|
||||
);
|
||||
|
||||
const handleToggleSubscribeClick = useCallback(() => {
|
||||
onUpdate({
|
||||
isSubscribed: !isSubscribed,
|
||||
|
@ -127,6 +120,7 @@ const CardModal = React.memo(
|
|||
|
||||
return (
|
||||
<Modal open closeIcon size="small" centered={false} onClose={onClose}>
|
||||
<AddAttachmentZone onCreate={onAttachmentCreate}>
|
||||
<Grid className={styles.grid}>
|
||||
<Grid.Row className={styles.headerPadding}>
|
||||
<Grid.Column width={16} className={styles.headerPadding}>
|
||||
|
@ -343,16 +337,20 @@ const CardModal = React.memo(
|
|||
{t('common.timer')}
|
||||
</Button>
|
||||
</EditTimerPopup>
|
||||
<FilePicker onSelect={handleAttachmentFileSelect}>
|
||||
<AddAttachment onCreate={onAttachmentCreate}>
|
||||
<Button fluid className={styles.actionButton}>
|
||||
<Icon name="attach" className={styles.actionIcon} />
|
||||
{t('common.attachment')}
|
||||
</Button>
|
||||
</FilePicker>
|
||||
</AddAttachment>
|
||||
</div>
|
||||
<div className={styles.actions}>
|
||||
<span className={styles.actionsTitle}>{t('common.actions')}</span>
|
||||
<Button fluid className={styles.actionButton} onClick={handleToggleSubscribeClick}>
|
||||
<Button
|
||||
fluid
|
||||
className={styles.actionButton}
|
||||
onClick={handleToggleSubscribeClick}
|
||||
>
|
||||
<Icon name="paper plane outline" className={styles.actionIcon} />
|
||||
{isSubscribed ? t('action.unsubscribe') : t('action.subscribe')}
|
||||
</Button>
|
||||
|
@ -373,6 +371,7 @@ const CardModal = React.memo(
|
|||
</Grid.Column>
|
||||
</Grid.Row>
|
||||
</Grid>
|
||||
</AddAttachmentZone>
|
||||
</Modal>
|
||||
);
|
||||
},
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import isEmail from 'validator/lib/isEmail';
|
||||
import React, { useCallback, useEffect, useMemo, useRef } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import classNames from 'classnames';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import isEmail from 'validator/lib/isEmail';
|
||||
import { Form, Grid, Header, Message } from 'semantic-ui-react';
|
||||
import { useDidUpdate, usePrevious, useToggle } from '../../lib/hooks';
|
||||
import { Input } from '../../lib/custom-ui';
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import useField from './use-field';
|
||||
import useForm from './use-form';
|
||||
import useSteps from './use-steps';
|
||||
import useModal from './use-modal';
|
||||
import useClosableForm from './use-closable-form';
|
||||
|
||||
export { useField, useForm, useSteps, useClosableForm };
|
||||
export { useField, useForm, useSteps, useModal, useClosableForm };
|
||||
|
|
15
client/src/hooks/use-modal.js
Normal file
15
client/src/hooks/use-modal.js
Normal file
|
@ -0,0 +1,15 @@
|
|||
import { useCallback, useState } from 'react';
|
||||
|
||||
export default (initialParams) => {
|
||||
const [modal, setModal] = useState(() => initialParams);
|
||||
|
||||
const open = useCallback((params) => {
|
||||
setModal(params);
|
||||
}, []);
|
||||
|
||||
const handleClose = useCallback(() => {
|
||||
setModal(null);
|
||||
}, []);
|
||||
|
||||
return [modal, open, handleClose];
|
||||
};
|
|
@ -14,7 +14,7 @@ const createStep = (type, params = {}) => {
|
|||
export default (initialType, initialParams) => {
|
||||
const [step, setStep] = useState(() => createStep(initialType, initialParams));
|
||||
|
||||
const openStep = useCallback((type, params) => {
|
||||
const open = useCallback((type, params) => {
|
||||
setStep(createStep(type, params));
|
||||
}, []);
|
||||
|
||||
|
@ -22,5 +22,5 @@ export default (initialType, initialParams) => {
|
|||
setStep(null);
|
||||
}, []);
|
||||
|
||||
return [step, openStep, handleBack];
|
||||
return [step, open, handleBack];
|
||||
};
|
||||
|
|
4
client/src/lib/custom-ui/index.css
vendored
4
client/src/lib/custom-ui/index.css
vendored
|
@ -34694,11 +34694,11 @@ select.ui.dropdown {
|
|||
overflow: hidden;
|
||||
}
|
||||
|
||||
.scrolling.dimmable > .dimmer {
|
||||
/* .scrolling.dimmable > .dimmer {
|
||||
-webkit-box-pack: start;
|
||||
-ms-flex-pack: start;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
} */
|
||||
|
||||
.scrolling.dimmable.dimmed > .dimmer {
|
||||
overflow: auto;
|
||||
|
|
|
@ -42,6 +42,7 @@ export default {
|
|||
createLabel_title: 'Create Label',
|
||||
createNewOneOrSelectExistingOne: 'Create a new one or select<br />an existing one',
|
||||
createProject_title: 'Create Project',
|
||||
createTextFile_title: 'Create Text File',
|
||||
currentPassword: 'Current password',
|
||||
date: 'Date',
|
||||
dueDate: 'Due date',
|
||||
|
@ -55,6 +56,7 @@ export default {
|
|||
deleteTask_title: 'Delete Task',
|
||||
deleteUser_title: 'Delete User',
|
||||
description: 'Description',
|
||||
dropFileToUpload: 'Drop file to upload',
|
||||
editAttachment_title: 'Edit Attachment',
|
||||
editAvatar_title: 'Edit Avatar',
|
||||
editBoard_title: 'Edit Board',
|
||||
|
@ -69,6 +71,7 @@ export default {
|
|||
emailAlreadyInUse: 'E-mail already in use',
|
||||
enterCardTitle: 'Enter card title...',
|
||||
enterDescription: 'Enter description...',
|
||||
enterFilename: 'Enter filename',
|
||||
enterListTitle: 'Enter list title...',
|
||||
enterProjectTitle: 'Enter project title',
|
||||
enterTaskDescription: 'Enter task description...',
|
||||
|
@ -129,6 +132,7 @@ export default {
|
|||
addToCard: 'Add to card',
|
||||
addUser: 'Add user',
|
||||
createBoard: 'Create board',
|
||||
createFile: 'Create file',
|
||||
createLabel: 'Create label',
|
||||
createNewLabel: 'Create new label',
|
||||
createProject: 'Create project',
|
||||
|
|
|
@ -46,6 +46,7 @@ export default {
|
|||
createLabel: 'Создание метки',
|
||||
createNewOneOrSelectExistingOne: 'Создайте новую или выберите<br />уже существующую',
|
||||
createProject: 'Создание проекта',
|
||||
createTextFile: 'Создание текстового файла',
|
||||
currentPassword: 'Текущий пароль',
|
||||
date: 'Дата',
|
||||
dueDate: 'Срок',
|
||||
|
@ -59,6 +60,7 @@ export default {
|
|||
deleteTask: 'Удаление задачи',
|
||||
deleteUser: 'Удаление пользователя',
|
||||
description: 'Описание',
|
||||
dropFileToUpload: 'Перетяните файл, чтобы загрузить',
|
||||
editAttachment: 'Изменение вложения',
|
||||
editAvatar: 'Изменение аватара',
|
||||
editBoard: 'Изменение доски',
|
||||
|
@ -73,6 +75,7 @@ export default {
|
|||
emailAlreadyInUse: 'E-mail уже занят',
|
||||
enterCardTitle: 'Введите заголовок для этой карточки...',
|
||||
enterDescription: 'Введите описание...',
|
||||
enterFilename: 'Введите название файла',
|
||||
enterListTitle: 'Введите заголовок списка...',
|
||||
enterProjectTitle: 'Введите название проекта',
|
||||
enterTaskDescription: 'Введите описание задачи...',
|
||||
|
@ -133,6 +136,7 @@ export default {
|
|||
addToCard: 'Добавить на карточку',
|
||||
addUser: 'Добавить пользователя',
|
||||
createBoard: 'Создать доску',
|
||||
createFile: 'Создать файл',
|
||||
createLabel: 'Создать метку',
|
||||
createNewLabel: 'Создать новую метку',
|
||||
createProject: 'Создать проект',
|
||||
|
|
|
@ -60,7 +60,7 @@ module.exports = {
|
|||
dirname: file.extra.dirname,
|
||||
filename: file.filename,
|
||||
isImage: file.extra.isImage,
|
||||
name: file.filename,
|
||||
name: file.extra.name,
|
||||
},
|
||||
inputs.requestId,
|
||||
this.req,
|
||||
|
|
|
@ -3,6 +3,7 @@ const path = require('path');
|
|||
const util = require('util');
|
||||
const stream = require('stream');
|
||||
const streamToArray = require('stream-to-array');
|
||||
const filenamify = require('filenamify');
|
||||
const { v4: uuid } = require('uuid');
|
||||
const sharp = require('sharp');
|
||||
|
||||
|
@ -33,10 +34,13 @@ module.exports = {
|
|||
try {
|
||||
const dirname = uuid();
|
||||
|
||||
// FIXME: https://github.com/sindresorhus/filenamify/issues/13
|
||||
const filename = filenamify(file.filename);
|
||||
|
||||
const rootPath = path.join(sails.config.custom.attachmentsPath, dirname);
|
||||
fs.mkdirSync(rootPath);
|
||||
|
||||
await writeFile(path.join(rootPath, file.filename), buffer);
|
||||
await writeFile(path.join(rootPath, filename), buffer);
|
||||
|
||||
const image = sharp(buffer);
|
||||
let imageMetadata;
|
||||
|
@ -68,8 +72,12 @@ module.exports = {
|
|||
file.extra = {
|
||||
dirname,
|
||||
isImage: !!imageMetadata,
|
||||
name: file.filename,
|
||||
};
|
||||
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
file.filename = filename;
|
||||
|
||||
return done();
|
||||
} catch (error) {
|
||||
return done(error);
|
||||
|
|
31
server/package-lock.json
generated
31
server/package-lock.json
generated
|
@ -2386,6 +2386,21 @@
|
|||
"resolved": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz",
|
||||
"integrity": "sha1-wcS5vuPglyXdsQa3XB4wH+LxiyY="
|
||||
},
|
||||
"filename-reserved-regex": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-2.0.0.tgz",
|
||||
"integrity": "sha1-q/c9+rc10EVECr/qLZHzieu/oik="
|
||||
},
|
||||
"filenamify": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/filenamify/-/filenamify-4.1.0.tgz",
|
||||
"integrity": "sha512-KQV/uJDI9VQgN7sHH1Zbk6+42cD6mnQ2HONzkXUfPJ+K2FC8GZ1dpewbbHw0Sz8Tf5k3EVdHVayM4DoAwWlmtg==",
|
||||
"requires": {
|
||||
"filename-reserved-regex": "^2.0.0",
|
||||
"strip-outer": "^1.0.1",
|
||||
"trim-repeated": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"fill-range": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz",
|
||||
|
@ -6450,6 +6465,14 @@
|
|||
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
|
||||
"integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo="
|
||||
},
|
||||
"strip-outer": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/strip-outer/-/strip-outer-1.0.1.tgz",
|
||||
"integrity": "sha512-k55yxKHwaXnpYGsOzg4Vl8+tDrWylxDEpknGjhTiZB8dFRU5rTo9CAzeycivxV3s+zlTKwrs6WxMxR95n26kwg==",
|
||||
"requires": {
|
||||
"escape-string-regexp": "^1.0.2"
|
||||
}
|
||||
},
|
||||
"supports-color": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
|
||||
|
@ -6674,6 +6697,14 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"trim-repeated": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/trim-repeated/-/trim-repeated-1.0.0.tgz",
|
||||
"integrity": "sha1-42RqLqTokTEr9+rObPsFOAvAHCE=",
|
||||
"requires": {
|
||||
"escape-string-regexp": "^1.0.2"
|
||||
}
|
||||
},
|
||||
"tslib": {
|
||||
"version": "1.11.1",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.11.1.tgz",
|
||||
|
|
|
@ -42,6 +42,7 @@
|
|||
"bcrypt": "^4.0.1",
|
||||
"dotenv": "^8.2.0",
|
||||
"dotenv-cli": "^3.1.0",
|
||||
"filenamify": "^4.1.0",
|
||||
"jsonwebtoken": "^8.5.1",
|
||||
"knex": "^0.20.13",
|
||||
"lodash": "^4.17.15",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue