Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1a6fc40804 | ||
|
|
42092efcd2 | ||
|
|
5c91bddfe7 | ||
|
|
fd8f40f1b0 | ||
|
|
5b64d465e2 | ||
|
|
212fb8ac8c | ||
|
|
cece2254d7 |
@@ -7,6 +7,7 @@ import { useDidUpdate, usePrevious, useToggle } from '../../lib/hooks';
|
|||||||
import { Input, Popup } from '../../lib/custom-ui';
|
import { Input, Popup } from '../../lib/custom-ui';
|
||||||
|
|
||||||
import { useForm } from '../../hooks';
|
import { useForm } from '../../hooks';
|
||||||
|
import { isPassword } from '../../utils/validator';
|
||||||
|
|
||||||
import styles from './UserPasswordEditStep.module.scss';
|
import styles from './UserPasswordEditStep.module.scss';
|
||||||
|
|
||||||
@@ -56,7 +57,7 @@ const UserPasswordEditStep = React.memo(
|
|||||||
const currentPasswordField = useRef(null);
|
const currentPasswordField = useRef(null);
|
||||||
|
|
||||||
const handleSubmit = useCallback(() => {
|
const handleSubmit = useCallback(() => {
|
||||||
if (!data.password) {
|
if (!data.password || !isPassword(data.password)) {
|
||||||
passwordField.current.select();
|
passwordField.current.select();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -112,14 +113,18 @@ const UserPasswordEditStep = React.memo(
|
|||||||
)}
|
)}
|
||||||
<Form onSubmit={handleSubmit}>
|
<Form onSubmit={handleSubmit}>
|
||||||
<div className={styles.text}>{t('common.newPassword')}</div>
|
<div className={styles.text}>{t('common.newPassword')}</div>
|
||||||
<Input.Password
|
<div className={styles.field}>
|
||||||
fluid
|
<Input.Password
|
||||||
ref={passwordField}
|
fluid
|
||||||
name="password"
|
ref={passwordField}
|
||||||
value={data.password}
|
name="password"
|
||||||
className={styles.field}
|
value={data.password}
|
||||||
onChange={handleFieldChange}
|
onChange={handleFieldChange}
|
||||||
/>
|
/>
|
||||||
|
<div className={styles.note}>
|
||||||
|
{t('common.mustBeAtLeast6CharactersLongAndContainAtLeastOneLetterAndNumber')}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
{usePasswordConfirmation && (
|
{usePasswordConfirmation && (
|
||||||
<>
|
<>
|
||||||
<div className={styles.text}>{t('common.currentPassword')}</div>
|
<div className={styles.text}>{t('common.currentPassword')}</div>
|
||||||
|
|||||||
@@ -3,6 +3,12 @@
|
|||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.note {
|
||||||
|
font-size: 11px;
|
||||||
|
margin-top: 4px;
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
.text {
|
.text {
|
||||||
color: #444444;
|
color: #444444;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
:global(#app) {
|
:global(#app) {
|
||||||
.action {
|
.action {
|
||||||
border: none;
|
border: none;
|
||||||
|
border-radius: 0.28571429rem;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
height: 36px;
|
height: 36px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|||||||
@@ -117,7 +117,7 @@ export default {
|
|||||||
phone: 'Telefon',
|
phone: 'Telefon',
|
||||||
preferences: 'Volby',
|
preferences: 'Volby',
|
||||||
pressPasteShortcutToAddAttachmentFromClipboard:
|
pressPasteShortcutToAddAttachmentFromClipboard:
|
||||||
'Tip: dejte Ctrl-V (Cmd-V na Mac) pro vložení přílohy ze schránky.',
|
'Tip: dejte Ctrl-V (Cmd-V na Mac) pro vložení přílohy ze schránky',
|
||||||
project: 'Projekt',
|
project: 'Projekt',
|
||||||
projectNotFound_title: 'Projekt nenalezen',
|
projectNotFound_title: 'Projekt nenalezen',
|
||||||
removeManager_title: 'Odstranit vedoucího',
|
removeManager_title: 'Odstranit vedoucího',
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ export default {
|
|||||||
administrator: 'Administrator',
|
administrator: 'Administrator',
|
||||||
all: 'Alle',
|
all: 'Alle',
|
||||||
allChangesWillBeAutomaticallySavedAfterConnectionRestored:
|
allChangesWillBeAutomaticallySavedAfterConnectionRestored:
|
||||||
'Alle Änderungen werden automatisch gespeichert,<br />sobald die Verbindung wiederhergestellt wurde.',
|
'Alle Änderungen werden automatisch gespeichert,<br />sobald die Verbindung wiederhergestellt wurde',
|
||||||
areYouSureYouWantToDeleteThisAttachment:
|
areYouSureYouWantToDeleteThisAttachment:
|
||||||
'Sind Sie sicher, dass Sie diesen Anhang löschen möchten?',
|
'Sind Sie sicher, dass Sie diesen Anhang löschen möchten?',
|
||||||
areYouSureYouWantToDeleteThisBoard: 'Sind Sie sicher, dass Sie dieses Board löschen möchten?',
|
areYouSureYouWantToDeleteThisBoard: 'Sind Sie sicher, dass Sie dieses Board löschen möchten?',
|
||||||
@@ -123,7 +123,7 @@ export default {
|
|||||||
phone: 'Telefon',
|
phone: 'Telefon',
|
||||||
preferences: 'Voreinstellungen',
|
preferences: 'Voreinstellungen',
|
||||||
pressPasteShortcutToAddAttachmentFromClipboard:
|
pressPasteShortcutToAddAttachmentFromClipboard:
|
||||||
'Tipp: Drücken Sie STRG-V (Cmd-V auf Mac), um einen Anhang aus der Zwischenablage hinzuzufügen.',
|
'Tipp: Drücken Sie STRG-V (Cmd-V auf Mac), um einen Anhang aus der Zwischenablage hinzuzufügen',
|
||||||
project: 'Projekt',
|
project: 'Projekt',
|
||||||
projectNotFound_title: 'Projekt nicht gefunden',
|
projectNotFound_title: 'Projekt nicht gefunden',
|
||||||
removeManager_title: 'Manager entfernen',
|
removeManager_title: 'Manager entfernen',
|
||||||
|
|||||||
@@ -105,6 +105,8 @@ export default {
|
|||||||
members: 'Members',
|
members: 'Members',
|
||||||
minutes: 'Minutes',
|
minutes: 'Minutes',
|
||||||
moveCard_title: 'Move Card',
|
moveCard_title: 'Move Card',
|
||||||
|
mustBeAtLeast6CharactersLongAndContainAtLeastOneLetterAndNumber:
|
||||||
|
'Must be at least 6 characters long and contain at least one letter and number',
|
||||||
name: 'Name',
|
name: 'Name',
|
||||||
newEmail: 'New e-mail',
|
newEmail: 'New e-mail',
|
||||||
newPassword: 'New password',
|
newPassword: 'New password',
|
||||||
@@ -121,7 +123,7 @@ export default {
|
|||||||
phone: 'Phone',
|
phone: 'Phone',
|
||||||
preferences: 'Preferences',
|
preferences: 'Preferences',
|
||||||
pressPasteShortcutToAddAttachmentFromClipboard:
|
pressPasteShortcutToAddAttachmentFromClipboard:
|
||||||
'Tip: press Ctrl-V (Cmd-V on Mac) to add an attachment from the clipboard.',
|
'Tip: press Ctrl-V (Cmd-V on Mac) to add an attachment from the clipboard',
|
||||||
project: 'Project',
|
project: 'Project',
|
||||||
projectNotFound_title: 'Project Not Found',
|
projectNotFound_title: 'Project Not Found',
|
||||||
removeManager_title: 'Remove Manager',
|
removeManager_title: 'Remove Manager',
|
||||||
|
|||||||
@@ -106,7 +106,7 @@ export default {
|
|||||||
phone: 'Teléfono',
|
phone: 'Teléfono',
|
||||||
preferences: 'Preferencias',
|
preferences: 'Preferencias',
|
||||||
pressPasteShortcutToAddAttachmentFromClipboard:
|
pressPasteShortcutToAddAttachmentFromClipboard:
|
||||||
'Tip: presiona Ctrl-V (Cmd-V en Mac) para añadir adjuntos desde el portapapeles.',
|
'Tip: presiona Ctrl-V (Cmd-V en Mac) para añadir adjuntos desde el portapapeles',
|
||||||
project: 'Proyecto',
|
project: 'Proyecto',
|
||||||
projectNotFound_title: 'Proyecto no encontrado',
|
projectNotFound_title: 'Proyecto no encontrado',
|
||||||
removeMember_title: 'Remover Miembro',
|
removeMember_title: 'Remover Miembro',
|
||||||
@@ -115,7 +115,7 @@ export default {
|
|||||||
selectList: 'Seleccionar Lista',
|
selectList: 'Seleccionar Lista',
|
||||||
selectProject: 'Seleccionar Proyecto',
|
selectProject: 'Seleccionar Proyecto',
|
||||||
settings: 'Configuraciones',
|
settings: 'Configuraciones',
|
||||||
subscribeToMyOwnCardsByDefault: 'Suscribirme a mis propias tarjetas por defecto.',
|
subscribeToMyOwnCardsByDefault: 'Suscribirme a mis propias tarjetas por defecto',
|
||||||
taskActions_title: 'Acciones de Tareas',
|
taskActions_title: 'Acciones de Tareas',
|
||||||
tasks: 'Tareas',
|
tasks: 'Tareas',
|
||||||
time: 'Tiempo',
|
time: 'Tiempo',
|
||||||
|
|||||||
@@ -121,7 +121,7 @@ export default {
|
|||||||
phone: 'Telefono',
|
phone: 'Telefono',
|
||||||
preferences: 'Preferenze',
|
preferences: 'Preferenze',
|
||||||
pressPasteShortcutToAddAttachmentFromClipboard:
|
pressPasteShortcutToAddAttachmentFromClipboard:
|
||||||
'Tip: prmi Ctrl-V (Cmd-V on Mac) per aggiungere un allegato dalla clipboard.',
|
'Tip: prmi Ctrl-V (Cmd-V on Mac) per aggiungere un allegato dalla clipboard',
|
||||||
project: 'Progetto',
|
project: 'Progetto',
|
||||||
projectNotFound_title: 'Progetto non trovato',
|
projectNotFound_title: 'Progetto non trovato',
|
||||||
removeManager_title: 'Rimuovi Manager',
|
removeManager_title: 'Rimuovi Manager',
|
||||||
|
|||||||
@@ -117,7 +117,7 @@ export default {
|
|||||||
phone: '電話番号',
|
phone: '電話番号',
|
||||||
preferences: '環境設定',
|
preferences: '環境設定',
|
||||||
pressPasteShortcutToAddAttachmentFromClipboard:
|
pressPasteShortcutToAddAttachmentFromClipboard:
|
||||||
'ヒント: Ctrl-V(MacではCmd-V)を押して、クリップボードから添付ファイルを追加します。',
|
'ヒント: Ctrl-V(MacではCmd-V)を押して、クリップボードから添付ファイルを追加します',
|
||||||
project: 'プロジェクト',
|
project: 'プロジェクト',
|
||||||
projectNotFound_title: 'プロジェクトがありません',
|
projectNotFound_title: 'プロジェクトがありません',
|
||||||
removeManager_title: 'マネージャーを削除',
|
removeManager_title: 'マネージャーを削除',
|
||||||
|
|||||||
@@ -106,7 +106,7 @@ export default {
|
|||||||
phone: 'Telefon',
|
phone: 'Telefon',
|
||||||
preferences: 'Preferencje',
|
preferences: 'Preferencje',
|
||||||
pressPasteShortcutToAddAttachmentFromClipboard:
|
pressPasteShortcutToAddAttachmentFromClipboard:
|
||||||
'Podpowiedź: naciśnij Ctrl-V (Cmd-V na Macu) aby dodać załącznik ze schowka.',
|
'Podpowiedź: naciśnij Ctrl-V (Cmd-V na Macu) aby dodać załącznik ze schowka',
|
||||||
project: 'Projekt',
|
project: 'Projekt',
|
||||||
projectNotFound_title: 'Projekt Nie Znaleziony',
|
projectNotFound_title: 'Projekt Nie Znaleziony',
|
||||||
removeMember_title: 'Usuń Członka',
|
removeMember_title: 'Usuń Członka',
|
||||||
|
|||||||
@@ -100,6 +100,8 @@ export default {
|
|||||||
members: 'Участники',
|
members: 'Участники',
|
||||||
minutes: 'Минуты',
|
minutes: 'Минуты',
|
||||||
moveCard: 'Перемещение карточки',
|
moveCard: 'Перемещение карточки',
|
||||||
|
mustBeAtLeast6CharactersLongAndContainAtLeastOneLetterAndNumber:
|
||||||
|
'Должен быть не менее 6 символов и содержать хотя бы одну букву и цифру',
|
||||||
name: 'Имя',
|
name: 'Имя',
|
||||||
newEmail: 'Новый e-mail',
|
newEmail: 'Новый e-mail',
|
||||||
newPassword: 'Новый пароль',
|
newPassword: 'Новый пароль',
|
||||||
@@ -116,7 +118,7 @@ export default {
|
|||||||
phone: 'Телефон',
|
phone: 'Телефон',
|
||||||
preferences: 'Предпочтения',
|
preferences: 'Предпочтения',
|
||||||
pressPasteShortcutToAddAttachmentFromClipboard:
|
pressPasteShortcutToAddAttachmentFromClipboard:
|
||||||
'Совет: нажмите Ctrl-V (Cmd-V на Mac), чтобы добавить вложение из буфера обмена.',
|
'Совет: нажмите Ctrl-V (Cmd-V на Mac), чтобы добавить вложение из буфера обмена',
|
||||||
project: 'Проект',
|
project: 'Проект',
|
||||||
projectActions: 'Действия с проектом',
|
projectActions: 'Действия с проектом',
|
||||||
projectNotFound: 'Проект не найден',
|
projectNotFound: 'Проект не найден',
|
||||||
|
|||||||
@@ -118,7 +118,7 @@ export default {
|
|||||||
phone: 'Telefón',
|
phone: 'Telefón',
|
||||||
preferences: 'Voľby',
|
preferences: 'Voľby',
|
||||||
pressPasteShortcutToAddAttachmentFromClipboard:
|
pressPasteShortcutToAddAttachmentFromClipboard:
|
||||||
'Tip: stlačte Ctrl-V (Cmd-V na Mac) pre vloženie prílohy zo schránky.',
|
'Tip: stlačte Ctrl-V (Cmd-V na Mac) pre vloženie prílohy zo schránky',
|
||||||
project: 'Projekt',
|
project: 'Projekt',
|
||||||
projectNotFound_title: 'Projekt neexistuje',
|
projectNotFound_title: 'Projekt neexistuje',
|
||||||
removeManager_title: 'Odstrániť správcu',
|
removeManager_title: 'Odstrániť správcu',
|
||||||
|
|||||||
@@ -119,7 +119,7 @@ export default {
|
|||||||
phone: 'Telefon',
|
phone: 'Telefon',
|
||||||
preferences: 'Preferenser',
|
preferences: 'Preferenser',
|
||||||
pressPasteShortcutToAddAttachmentFromClipboard:
|
pressPasteShortcutToAddAttachmentFromClipboard:
|
||||||
'Tips: tryck på Ctrl-V (Cmd-V på Mac) för att lägga till en bilaga från urklipp.',
|
'Tips: tryck på Ctrl-V (Cmd-V på Mac) för att lägga till en bilaga från urklipp',
|
||||||
project: 'Projekt',
|
project: 'Projekt',
|
||||||
projectNotFound_title: 'Projekt Ej Funnet',
|
projectNotFound_title: 'Projekt Ej Funnet',
|
||||||
removeManager_title: 'Ta Bort Projektledare',
|
removeManager_title: 'Ta Bort Projektledare',
|
||||||
|
|||||||
@@ -112,7 +112,7 @@ export default {
|
|||||||
phone: 'Telefon',
|
phone: 'Telefon',
|
||||||
preferences: 'Afzalliklar',
|
preferences: 'Afzalliklar',
|
||||||
pressPasteShortcutToAddAttachmentFromClipboard:
|
pressPasteShortcutToAddAttachmentFromClipboard:
|
||||||
"Tip: Buferdan ilova qo'shish uchun Ctrl-V (Mac da Cmd-V) ni bosing.",
|
"Tip: Buferdan ilova qo'shish uchun Ctrl-V (Mac da Cmd-V) ni bosing",
|
||||||
project: 'Loyiha',
|
project: 'Loyiha',
|
||||||
projectNotFound_title: 'Loyiha Topilmadi',
|
projectNotFound_title: 'Loyiha Topilmadi',
|
||||||
removeManager_title: "Boshqaruvchini O'chirish",
|
removeManager_title: "Boshqaruvchini O'chirish",
|
||||||
|
|||||||
@@ -110,7 +110,7 @@ export default {
|
|||||||
phone: '电话',
|
phone: '电话',
|
||||||
preferences: '偏好',
|
preferences: '偏好',
|
||||||
pressPasteShortcutToAddAttachmentFromClipboard:
|
pressPasteShortcutToAddAttachmentFromClipboard:
|
||||||
'提示: 点击 Ctrl-V (Mac: Cmd-V) 从剪切板添加附件.',
|
'提示: 点击 Ctrl-V (Mac: Cmd-V) 从剪切板添加附件',
|
||||||
project: '项目',
|
project: '项目',
|
||||||
projectNotFound_title: '项目未找到',
|
projectNotFound_title: '项目未找到',
|
||||||
removeManager_title: '删除管理员',
|
removeManager_title: '删除管理员',
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
|
const PASSWORD_REGEX = /^(?=.*[A-Za-z])(?=.*\d).+$/;
|
||||||
const USERNAME_REGEX = /^[a-zA-Z0-9]+((_|\.)?[a-zA-Z0-9])*$/;
|
const USERNAME_REGEX = /^[a-zA-Z0-9]+((_|\.)?[a-zA-Z0-9])*$/;
|
||||||
|
|
||||||
// eslint-disable-next-line import/prefer-default-export
|
export const isPassword = (string) => {
|
||||||
|
return string.length >= 3 && PASSWORD_REGEX.test(string);
|
||||||
|
};
|
||||||
|
|
||||||
export const isUsername = (string) => {
|
export const isUsername = (string) => {
|
||||||
return string.length >= 3 && string.length <= 16 && USERNAME_REGEX.test(string);
|
return string.length >= 3 && string.length <= 16 && USERNAME_REGEX.test(string);
|
||||||
};
|
};
|
||||||
|
|||||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "planka",
|
"name": "planka",
|
||||||
"version": "1.7.1",
|
"version": "1.7.3",
|
||||||
"lockfileVersion": 2,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "planka",
|
"name": "planka",
|
||||||
"version": "1.7.1",
|
"version": "1.7.3",
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "planka",
|
"name": "planka",
|
||||||
"version": "1.7.1",
|
"version": "1.7.3",
|
||||||
"private": true,
|
"private": true,
|
||||||
"homepage": "https://plankanban.github.io/planka",
|
"homepage": "https://plankanban.github.io/planka",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
const util = require('util');
|
||||||
|
const { v4: uuid } = require('uuid');
|
||||||
|
|
||||||
const Errors = {
|
const Errors = {
|
||||||
NOT_ENOUGH_RIGHTS: {
|
NOT_ENOUGH_RIGHTS: {
|
||||||
notEnoughRights: 'Not enough rights',
|
notEnoughRights: 'Not enough rights',
|
||||||
@@ -5,6 +8,9 @@ const Errors = {
|
|||||||
CARD_NOT_FOUND: {
|
CARD_NOT_FOUND: {
|
||||||
cardNotFound: 'Card not found',
|
cardNotFound: 'Card not found',
|
||||||
},
|
},
|
||||||
|
NO_FILE_WAS_UPLOADED: {
|
||||||
|
noFileWasUploaded: 'No file was uploaded',
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
@@ -27,6 +33,9 @@ module.exports = {
|
|||||||
cardNotFound: {
|
cardNotFound: {
|
||||||
responseType: 'notFound',
|
responseType: 'notFound',
|
||||||
},
|
},
|
||||||
|
noFileWasUploaded: {
|
||||||
|
responseType: 'unprocessableEntity',
|
||||||
|
},
|
||||||
uploadError: {
|
uploadError: {
|
||||||
responseType: 'unprocessableEntity',
|
responseType: 'unprocessableEntity',
|
||||||
},
|
},
|
||||||
@@ -52,33 +61,37 @@ module.exports = {
|
|||||||
throw Errors.NOT_ENOUGH_RIGHTS;
|
throw Errors.NOT_ENOUGH_RIGHTS;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.req
|
const upload = util.promisify((options, callback) =>
|
||||||
.file('file')
|
this.req.file('file').upload(options, (error, files) => callback(error, files)),
|
||||||
.upload(sails.helpers.utils.createAttachmentReceiver(), async (error, files) => {
|
);
|
||||||
if (error) {
|
|
||||||
return exits.uploadError(error.message);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (files.length === 0) {
|
let files;
|
||||||
return exits.uploadError('No file was uploaded');
|
try {
|
||||||
}
|
files = await upload({
|
||||||
|
saveAs: uuid(),
|
||||||
const file = files[0];
|
maxBytes: null,
|
||||||
|
|
||||||
const attachment = await sails.helpers.attachments.createOne(
|
|
||||||
{
|
|
||||||
...file.extra,
|
|
||||||
filename: file.filename,
|
|
||||||
},
|
|
||||||
currentUser,
|
|
||||||
card,
|
|
||||||
inputs.requestId,
|
|
||||||
this.req,
|
|
||||||
);
|
|
||||||
|
|
||||||
return exits.success({
|
|
||||||
item: attachment.toJSON(),
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
} catch (error) {
|
||||||
|
return exits.uploadError(error.message); // TODO: add error
|
||||||
|
}
|
||||||
|
|
||||||
|
if (files.length === 0) {
|
||||||
|
throw Errors.NO_FILE_WAS_UPLOADED;
|
||||||
|
}
|
||||||
|
|
||||||
|
const file = _.last(files);
|
||||||
|
const fileData = await sails.helpers.attachments.processUploadedFile(file);
|
||||||
|
|
||||||
|
const attachment = await sails.helpers.attachments.createOne(
|
||||||
|
fileData,
|
||||||
|
currentUser,
|
||||||
|
card,
|
||||||
|
inputs.requestId,
|
||||||
|
this.req,
|
||||||
|
);
|
||||||
|
|
||||||
|
return exits.success({
|
||||||
|
item: attachment,
|
||||||
|
});
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
async fn(inputs, exits) {
|
async fn(inputs) {
|
||||||
const { currentUser } = this.req;
|
const { currentUser } = this.req;
|
||||||
|
|
||||||
const { board } = await sails.helpers.boards
|
const { board } = await sails.helpers.boards
|
||||||
@@ -61,7 +61,7 @@ module.exports = {
|
|||||||
const tasks = await sails.helpers.cards.getTasks(cardIds);
|
const tasks = await sails.helpers.cards.getTasks(cardIds);
|
||||||
const attachments = await sails.helpers.cards.getAttachments(cardIds);
|
const attachments = await sails.helpers.cards.getAttachments(cardIds);
|
||||||
|
|
||||||
return exits.success({
|
return {
|
||||||
items: cards,
|
items: cards,
|
||||||
included: {
|
included: {
|
||||||
cardMemberships,
|
cardMemberships,
|
||||||
@@ -69,6 +69,6 @@ module.exports = {
|
|||||||
tasks,
|
tasks,
|
||||||
attachments,
|
attachments,
|
||||||
},
|
},
|
||||||
});
|
};
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,7 +1,17 @@
|
|||||||
|
const util = require('util');
|
||||||
|
const rimraf = require('rimraf');
|
||||||
|
const { v4: uuid } = require('uuid');
|
||||||
|
|
||||||
const Errors = {
|
const Errors = {
|
||||||
PROJECT_NOT_FOUND: {
|
PROJECT_NOT_FOUND: {
|
||||||
projectNotFound: 'Project not found',
|
projectNotFound: 'Project not found',
|
||||||
},
|
},
|
||||||
|
NO_FILE_WAS_UPLOADED: {
|
||||||
|
noFileWasUploaded: 'No file was uploaded',
|
||||||
|
},
|
||||||
|
FILE_IS_NOT_IMAGE: {
|
||||||
|
fileIsNotImage: 'File is not image',
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
@@ -17,6 +27,12 @@ module.exports = {
|
|||||||
projectNotFound: {
|
projectNotFound: {
|
||||||
responseType: 'notFound',
|
responseType: 'notFound',
|
||||||
},
|
},
|
||||||
|
noFileWasUploaded: {
|
||||||
|
responseType: 'unprocessableEntity',
|
||||||
|
},
|
||||||
|
fileIsNotImage: {
|
||||||
|
responseType: 'unprocessableEntity',
|
||||||
|
},
|
||||||
uploadError: {
|
uploadError: {
|
||||||
responseType: 'unprocessableEntity',
|
responseType: 'unprocessableEntity',
|
||||||
},
|
},
|
||||||
@@ -37,32 +53,52 @@ module.exports = {
|
|||||||
throw Errors.PROJECT_NOT_FOUND; // Forbidden
|
throw Errors.PROJECT_NOT_FOUND; // Forbidden
|
||||||
}
|
}
|
||||||
|
|
||||||
this.req
|
const upload = util.promisify((options, callback) =>
|
||||||
.file('file')
|
this.req.file('file').upload(options, (error, files) => callback(error, files)),
|
||||||
.upload(sails.helpers.utils.createProjectBackgroundImageReceiver(), async (error, files) => {
|
);
|
||||||
if (error) {
|
|
||||||
return exits.uploadError(error.message);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (files.length === 0) {
|
let files;
|
||||||
return exits.uploadError('No file was uploaded');
|
try {
|
||||||
}
|
files = await upload({
|
||||||
|
saveAs: uuid(),
|
||||||
project = await sails.helpers.projects.updateOne(
|
maxBytes: null,
|
||||||
project,
|
|
||||||
{
|
|
||||||
backgroundImageDirname: files[0].extra.dirname,
|
|
||||||
},
|
|
||||||
this.req,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!project) {
|
|
||||||
throw Errors.PROJECT_NOT_FOUND;
|
|
||||||
}
|
|
||||||
|
|
||||||
return exits.success({
|
|
||||||
item: project.toJSON(),
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
} catch (error) {
|
||||||
|
return exits.uploadError(error.message); // TODO: add error
|
||||||
|
}
|
||||||
|
|
||||||
|
if (files.length === 0) {
|
||||||
|
throw Errors.NO_FILE_WAS_UPLOADED;
|
||||||
|
}
|
||||||
|
|
||||||
|
const file = _.last(files);
|
||||||
|
|
||||||
|
const fileData = await sails.helpers.projects
|
||||||
|
.processUploadedBackgroundImageFile(file)
|
||||||
|
.intercept('fileIsNotImage', () => {
|
||||||
|
try {
|
||||||
|
rimraf.sync(file.fd);
|
||||||
|
} catch (error) {
|
||||||
|
console.warn(error.stack); // eslint-disable-line no-console
|
||||||
|
}
|
||||||
|
|
||||||
|
return Errors.FILE_IS_NOT_IMAGE;
|
||||||
|
});
|
||||||
|
|
||||||
|
project = await sails.helpers.projects.updateOne(
|
||||||
|
project,
|
||||||
|
{
|
||||||
|
backgroundImageDirname: fileData.dirname,
|
||||||
|
},
|
||||||
|
this.req,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!project) {
|
||||||
|
throw Errors.PROJECT_NOT_FOUND;
|
||||||
|
}
|
||||||
|
|
||||||
|
return exits.success({
|
||||||
|
item: project,
|
||||||
|
});
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -16,6 +16,8 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
password: {
|
password: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
|
minLength: 6,
|
||||||
|
regex: /^(?=.*[A-Za-z])(?=.*\d).+$/,
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
name: {
|
name: {
|
||||||
|
|||||||
@@ -1,7 +1,17 @@
|
|||||||
|
const util = require('util');
|
||||||
|
const rimraf = require('rimraf');
|
||||||
|
const { v4: uuid } = require('uuid');
|
||||||
|
|
||||||
const Errors = {
|
const Errors = {
|
||||||
USER_NOT_FOUND: {
|
USER_NOT_FOUND: {
|
||||||
userNotFound: 'User not found',
|
userNotFound: 'User not found',
|
||||||
},
|
},
|
||||||
|
NO_FILE_WAS_UPLOADED: {
|
||||||
|
noFileWasUploaded: 'No file was uploaded',
|
||||||
|
},
|
||||||
|
FILE_IS_NOT_IMAGE: {
|
||||||
|
fileIsNotImage: 'File is not image',
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
@@ -17,6 +27,12 @@ module.exports = {
|
|||||||
userNotFound: {
|
userNotFound: {
|
||||||
responseType: 'notFound',
|
responseType: 'notFound',
|
||||||
},
|
},
|
||||||
|
noFileWasUploaded: {
|
||||||
|
responseType: 'unprocessableEntity',
|
||||||
|
},
|
||||||
|
fileIsNotImage: {
|
||||||
|
responseType: 'unprocessableEntity',
|
||||||
|
},
|
||||||
uploadError: {
|
uploadError: {
|
||||||
responseType: 'unprocessableEntity',
|
responseType: 'unprocessableEntity',
|
||||||
},
|
},
|
||||||
@@ -38,33 +54,53 @@ module.exports = {
|
|||||||
user = currentUser;
|
user = currentUser;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.req
|
const upload = util.promisify((options, callback) =>
|
||||||
.file('file')
|
this.req.file('file').upload(options, (error, files) => callback(error, files)),
|
||||||
.upload(sails.helpers.utils.createUserAvatarReceiver(), async (error, files) => {
|
);
|
||||||
if (error) {
|
|
||||||
return exits.uploadError(error.message);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (files.length === 0) {
|
let files;
|
||||||
return exits.uploadError('No file was uploaded');
|
try {
|
||||||
}
|
files = await upload({
|
||||||
|
saveAs: uuid(),
|
||||||
user = await sails.helpers.users.updateOne(
|
maxBytes: null,
|
||||||
user,
|
|
||||||
{
|
|
||||||
avatarDirname: files[0].extra.dirname,
|
|
||||||
},
|
|
||||||
currentUser,
|
|
||||||
this.req,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!user) {
|
|
||||||
throw Errors.USER_NOT_FOUND;
|
|
||||||
}
|
|
||||||
|
|
||||||
return exits.success({
|
|
||||||
item: user.toJSON(),
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
} catch (error) {
|
||||||
|
return exits.uploadError(error.message); // TODO: add error
|
||||||
|
}
|
||||||
|
|
||||||
|
if (files.length === 0) {
|
||||||
|
throw Errors.NO_FILE_WAS_UPLOADED;
|
||||||
|
}
|
||||||
|
|
||||||
|
const file = _.last(files);
|
||||||
|
|
||||||
|
const fileData = await sails.helpers.users
|
||||||
|
.processUploadedAvatarFile(file)
|
||||||
|
.intercept('fileIsNotImage', () => {
|
||||||
|
try {
|
||||||
|
rimraf.sync(file.fd);
|
||||||
|
} catch (error) {
|
||||||
|
console.warn(error.stack); // eslint-disable-line no-console
|
||||||
|
}
|
||||||
|
|
||||||
|
return Errors.FILE_IS_NOT_IMAGE;
|
||||||
|
});
|
||||||
|
|
||||||
|
user = await sails.helpers.users.updateOne(
|
||||||
|
user,
|
||||||
|
{
|
||||||
|
avatarDirname: fileData.dirname,
|
||||||
|
},
|
||||||
|
currentUser,
|
||||||
|
this.req,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!user) {
|
||||||
|
throw Errors.USER_NOT_FOUND;
|
||||||
|
}
|
||||||
|
|
||||||
|
return exits.success({
|
||||||
|
item: user,
|
||||||
|
});
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -18,6 +18,8 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
password: {
|
password: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
|
minLength: 6,
|
||||||
|
regex: /^(?=.*[A-Za-z])(?=.*\d).+$/,
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
currentPassword: {
|
currentPassword: {
|
||||||
|
|||||||
@@ -0,0 +1,77 @@
|
|||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
const rimraf = require('rimraf');
|
||||||
|
const moveFile = require('move-file');
|
||||||
|
const filenamify = require('filenamify');
|
||||||
|
const { v4: uuid } = require('uuid');
|
||||||
|
const sharp = require('sharp');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
inputs: {
|
||||||
|
file: {
|
||||||
|
type: 'json',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
async fn(inputs) {
|
||||||
|
const dirname = uuid();
|
||||||
|
|
||||||
|
// FIXME: https://github.com/sindresorhus/filenamify/issues/13
|
||||||
|
const filename = filenamify(inputs.file.filename);
|
||||||
|
|
||||||
|
const rootPath = path.join(sails.config.custom.attachmentsPath, dirname);
|
||||||
|
const filePath = path.join(rootPath, filename);
|
||||||
|
|
||||||
|
fs.mkdirSync(rootPath);
|
||||||
|
await moveFile(inputs.file.fd, filePath);
|
||||||
|
|
||||||
|
const image = sharp(filePath);
|
||||||
|
let metadata;
|
||||||
|
|
||||||
|
try {
|
||||||
|
metadata = await image.metadata();
|
||||||
|
} catch (error) {} // eslint-disable-line no-empty
|
||||||
|
|
||||||
|
const fileData = {
|
||||||
|
dirname,
|
||||||
|
filename,
|
||||||
|
image: null,
|
||||||
|
name: inputs.file.filename,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (metadata && !['svg', 'pdf'].includes(metadata.format)) {
|
||||||
|
const thumbnailsPath = path.join(rootPath, 'thumbnails');
|
||||||
|
fs.mkdirSync(thumbnailsPath);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await image
|
||||||
|
.resize(
|
||||||
|
metadata.height > metadata.width
|
||||||
|
? {
|
||||||
|
width: 256,
|
||||||
|
height: 320,
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
width: 256,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.jpeg({
|
||||||
|
quality: 100,
|
||||||
|
chromaSubsampling: '4:4:4',
|
||||||
|
})
|
||||||
|
.toFile(path.join(thumbnailsPath, 'cover-256.jpg'));
|
||||||
|
|
||||||
|
fileData.image = _.pick(metadata, ['width', 'height']);
|
||||||
|
} catch (error1) {
|
||||||
|
try {
|
||||||
|
rimraf.sync(thumbnailsPath);
|
||||||
|
} catch (error2) {
|
||||||
|
console.warn(error2.stack); // eslint-disable-line no-console
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return fileData;
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
const rimraf = require('rimraf');
|
||||||
|
const { v4: uuid } = require('uuid');
|
||||||
|
const sharp = require('sharp');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
inputs: {
|
||||||
|
file: {
|
||||||
|
type: 'json',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
exits: {
|
||||||
|
fileIsNotImage: {},
|
||||||
|
},
|
||||||
|
|
||||||
|
async fn(inputs) {
|
||||||
|
const image = sharp(inputs.file.fd);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await image.metadata();
|
||||||
|
} catch (error) {
|
||||||
|
throw 'fileIsNotImage';
|
||||||
|
}
|
||||||
|
|
||||||
|
const dirname = uuid();
|
||||||
|
const rootPath = path.join(sails.config.custom.projectBackgroundImagesPath, dirname);
|
||||||
|
|
||||||
|
fs.mkdirSync(rootPath);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await image.jpeg().toFile(path.join(rootPath, 'original.jpg'));
|
||||||
|
|
||||||
|
await image
|
||||||
|
.resize(336, 200)
|
||||||
|
.jpeg({
|
||||||
|
quality: 100,
|
||||||
|
chromaSubsampling: '4:4:4',
|
||||||
|
})
|
||||||
|
.toFile(path.join(rootPath, 'cover-336.jpg'));
|
||||||
|
} catch (error1) {
|
||||||
|
try {
|
||||||
|
rimraf.sync(rootPath);
|
||||||
|
} catch (error2) {
|
||||||
|
console.warn(error2.stack); // eslint-disable-line no-console
|
||||||
|
}
|
||||||
|
|
||||||
|
throw 'fileIsNotImage';
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
rimraf.sync(inputs.file.fd);
|
||||||
|
} catch (error) {
|
||||||
|
console.warn(error.stack); // eslint-disable-line no-console
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
dirname,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
const rimraf = require('rimraf');
|
||||||
|
const { v4: uuid } = require('uuid');
|
||||||
|
const sharp = require('sharp');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
inputs: {
|
||||||
|
file: {
|
||||||
|
type: 'json',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
exits: {
|
||||||
|
fileIsNotImage: {},
|
||||||
|
},
|
||||||
|
|
||||||
|
async fn(inputs) {
|
||||||
|
const image = sharp(inputs.file.fd);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await image.metadata();
|
||||||
|
} catch (error) {
|
||||||
|
throw 'fileIsNotImage';
|
||||||
|
}
|
||||||
|
|
||||||
|
const dirname = uuid();
|
||||||
|
const rootPath = path.join(sails.config.custom.userAvatarsPath, dirname);
|
||||||
|
|
||||||
|
fs.mkdirSync(rootPath);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await image
|
||||||
|
.jpeg({
|
||||||
|
quality: 100,
|
||||||
|
chromaSubsampling: '4:4:4',
|
||||||
|
})
|
||||||
|
.toFile(path.join(rootPath, 'original.jpg'));
|
||||||
|
|
||||||
|
await image
|
||||||
|
.resize(100, 100)
|
||||||
|
.jpeg({
|
||||||
|
quality: 100,
|
||||||
|
chromaSubsampling: '4:4:4',
|
||||||
|
})
|
||||||
|
.toFile(path.join(rootPath, 'square-100.jpg'));
|
||||||
|
} catch (error1) {
|
||||||
|
try {
|
||||||
|
rimraf.sync(rootPath);
|
||||||
|
} catch (error2) {
|
||||||
|
console.warn(error2.stack); // eslint-disable-line no-console
|
||||||
|
}
|
||||||
|
|
||||||
|
throw 'fileIsNotImage';
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
rimraf.sync(inputs.file.fd);
|
||||||
|
} catch (error) {
|
||||||
|
console.warn(error.stack); // eslint-disable-line no-console
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
dirname,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -1,102 +0,0 @@
|
|||||||
const fs = require('fs');
|
|
||||||
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');
|
|
||||||
|
|
||||||
const writeFile = util.promisify(fs.writeFile);
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
sync: true,
|
|
||||||
|
|
||||||
fn() {
|
|
||||||
const receiver = stream.Writable({
|
|
||||||
objectMode: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
let firstFileHandled = false;
|
|
||||||
// eslint-disable-next-line no-underscore-dangle
|
|
||||||
receiver._write = async (file, receiverEncoding, done) => {
|
|
||||||
if (firstFileHandled) {
|
|
||||||
file.pipe(new stream.Writable());
|
|
||||||
|
|
||||||
return done();
|
|
||||||
}
|
|
||||||
firstFileHandled = true;
|
|
||||||
|
|
||||||
const buffer = await streamToArray(file).then((parts) =>
|
|
||||||
Buffer.concat(parts.map((part) => (Buffer.isBuffer(part) ? part : Buffer.from(part)))),
|
|
||||||
);
|
|
||||||
|
|
||||||
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, filename), buffer);
|
|
||||||
|
|
||||||
const image = sharp(buffer);
|
|
||||||
let metadata;
|
|
||||||
|
|
||||||
try {
|
|
||||||
metadata = await image.metadata();
|
|
||||||
} catch (error) {} // eslint-disable-line no-empty
|
|
||||||
|
|
||||||
const extra = {
|
|
||||||
dirname,
|
|
||||||
name: file.filename,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (metadata && !['svg', 'pdf'].includes(metadata.format)) {
|
|
||||||
let cover256Buffer;
|
|
||||||
if (metadata.height > metadata.width) {
|
|
||||||
cover256Buffer = await image
|
|
||||||
.resize(256, 320)
|
|
||||||
.jpeg({
|
|
||||||
quality: 100,
|
|
||||||
chromaSubsampling: '4:4:4',
|
|
||||||
})
|
|
||||||
.toBuffer();
|
|
||||||
} else {
|
|
||||||
cover256Buffer = await image
|
|
||||||
.resize({
|
|
||||||
width: 256,
|
|
||||||
})
|
|
||||||
.jpeg({
|
|
||||||
quality: 100,
|
|
||||||
chromaSubsampling: '4:4:4',
|
|
||||||
})
|
|
||||||
.toBuffer();
|
|
||||||
}
|
|
||||||
|
|
||||||
const thumbnailsPath = path.join(rootPath, 'thumbnails');
|
|
||||||
fs.mkdirSync(thumbnailsPath);
|
|
||||||
|
|
||||||
await writeFile(path.join(thumbnailsPath, 'cover-256.jpg'), cover256Buffer);
|
|
||||||
|
|
||||||
extra.image = _.pick(metadata, ['width', 'height']);
|
|
||||||
} else {
|
|
||||||
extra.image = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
Object.assign(file, {
|
|
||||||
filename,
|
|
||||||
extra,
|
|
||||||
});
|
|
||||||
|
|
||||||
return done();
|
|
||||||
} catch (error) {
|
|
||||||
return done(error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return receiver;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
@@ -1,65 +0,0 @@
|
|||||||
const fs = require('fs');
|
|
||||||
const path = require('path');
|
|
||||||
const util = require('util');
|
|
||||||
const stream = require('stream');
|
|
||||||
const streamToArray = require('stream-to-array');
|
|
||||||
const { v4: uuid } = require('uuid');
|
|
||||||
const sharp = require('sharp');
|
|
||||||
|
|
||||||
const writeFile = util.promisify(fs.writeFile);
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
sync: true,
|
|
||||||
|
|
||||||
fn() {
|
|
||||||
const receiver = stream.Writable({
|
|
||||||
objectMode: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
let firstFileHandled = false;
|
|
||||||
// eslint-disable-next-line no-underscore-dangle
|
|
||||||
receiver._write = async (file, receiverEncoding, done) => {
|
|
||||||
if (firstFileHandled) {
|
|
||||||
file.pipe(new stream.Writable());
|
|
||||||
|
|
||||||
return done();
|
|
||||||
}
|
|
||||||
firstFileHandled = true;
|
|
||||||
|
|
||||||
const buffer = await streamToArray(file).then((parts) =>
|
|
||||||
Buffer.concat(parts.map((part) => (Buffer.isBuffer(part) ? part : Buffer.from(part)))),
|
|
||||||
);
|
|
||||||
|
|
||||||
try {
|
|
||||||
const originalBuffer = await sharp(buffer).jpeg().toBuffer();
|
|
||||||
|
|
||||||
const cover336Buffer = await sharp(buffer)
|
|
||||||
.resize(336, 200)
|
|
||||||
.jpeg({
|
|
||||||
quality: 100,
|
|
||||||
chromaSubsampling: '4:4:4',
|
|
||||||
})
|
|
||||||
.toBuffer();
|
|
||||||
|
|
||||||
const dirname = uuid();
|
|
||||||
|
|
||||||
const rootPath = path.join(sails.config.custom.projectBackgroundImagesPath, dirname);
|
|
||||||
fs.mkdirSync(rootPath);
|
|
||||||
|
|
||||||
await writeFile(path.join(rootPath, 'original.jpg'), originalBuffer);
|
|
||||||
await writeFile(path.join(rootPath, 'cover-336.jpg'), cover336Buffer);
|
|
||||||
|
|
||||||
// eslint-disable-next-line no-param-reassign
|
|
||||||
file.extra = {
|
|
||||||
dirname,
|
|
||||||
};
|
|
||||||
|
|
||||||
return done();
|
|
||||||
} catch (error) {
|
|
||||||
return done(error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return receiver;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
@@ -1,70 +0,0 @@
|
|||||||
const fs = require('fs');
|
|
||||||
const path = require('path');
|
|
||||||
const util = require('util');
|
|
||||||
const stream = require('stream');
|
|
||||||
const streamToArray = require('stream-to-array');
|
|
||||||
const { v4: uuid } = require('uuid');
|
|
||||||
const sharp = require('sharp');
|
|
||||||
|
|
||||||
const writeFile = util.promisify(fs.writeFile);
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
sync: true,
|
|
||||||
|
|
||||||
fn() {
|
|
||||||
const receiver = stream.Writable({
|
|
||||||
objectMode: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
let firstFileHandled = false;
|
|
||||||
// eslint-disable-next-line no-underscore-dangle
|
|
||||||
receiver._write = async (file, receiverEncoding, done) => {
|
|
||||||
if (firstFileHandled) {
|
|
||||||
file.pipe(new stream.Writable());
|
|
||||||
|
|
||||||
return done();
|
|
||||||
}
|
|
||||||
firstFileHandled = true;
|
|
||||||
|
|
||||||
const buffer = await streamToArray(file).then((parts) =>
|
|
||||||
Buffer.concat(parts.map((part) => (Buffer.isBuffer(part) ? part : Buffer.from(part)))),
|
|
||||||
);
|
|
||||||
|
|
||||||
try {
|
|
||||||
const originalBuffer = await sharp(buffer)
|
|
||||||
.jpeg({
|
|
||||||
quality: 100,
|
|
||||||
chromaSubsampling: '4:4:4',
|
|
||||||
})
|
|
||||||
.toBuffer();
|
|
||||||
|
|
||||||
const square100Buffer = await sharp(buffer)
|
|
||||||
.resize(100, 100)
|
|
||||||
.jpeg({
|
|
||||||
quality: 100,
|
|
||||||
chromaSubsampling: '4:4:4',
|
|
||||||
})
|
|
||||||
.toBuffer();
|
|
||||||
|
|
||||||
const dirname = uuid();
|
|
||||||
|
|
||||||
const rootPath = path.join(sails.config.custom.userAvatarsPath, dirname);
|
|
||||||
fs.mkdirSync(rootPath);
|
|
||||||
|
|
||||||
await writeFile(path.join(rootPath, 'original.jpg'), originalBuffer);
|
|
||||||
await writeFile(path.join(rootPath, 'square-100.jpg'), square100Buffer);
|
|
||||||
|
|
||||||
// eslint-disable-next-line no-param-reassign
|
|
||||||
file.extra = {
|
|
||||||
dirname,
|
|
||||||
};
|
|
||||||
|
|
||||||
return done();
|
|
||||||
} catch (error) {
|
|
||||||
return done(error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return receiver;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
Generated
+38
@@ -14,6 +14,7 @@
|
|||||||
"knex": "^2.2.0",
|
"knex": "^2.2.0",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"moment": "^2.29.4",
|
"moment": "^2.29.4",
|
||||||
|
"move-file": "^2.1.0",
|
||||||
"rimraf": "^3.0.2",
|
"rimraf": "^3.0.2",
|
||||||
"sails": "^1.5.2",
|
"sails": "^1.5.2",
|
||||||
"sails-hook-orm": "^4.0.1",
|
"sails-hook-orm": "^4.0.1",
|
||||||
@@ -4398,6 +4399,28 @@
|
|||||||
"node": "*"
|
"node": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/move-file": {
|
||||||
|
"version": "2.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/move-file/-/move-file-2.1.0.tgz",
|
||||||
|
"integrity": "sha512-i9qLW6gqboJ5Ht8bauZi7KlTnQ3QFpBCvMvFfEcHADKgHGeJ9BZMO7SFCTwHPV9Qa0du9DYY1Yx3oqlGt30nXA==",
|
||||||
|
"dependencies": {
|
||||||
|
"path-exists": "^4.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10.17"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/move-file/node_modules/path-exists": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/ms": {
|
"node_modules/ms": {
|
||||||
"version": "2.1.2",
|
"version": "2.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||||
@@ -11370,6 +11393,21 @@
|
|||||||
"resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz",
|
"resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz",
|
||||||
"integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w=="
|
"integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w=="
|
||||||
},
|
},
|
||||||
|
"move-file": {
|
||||||
|
"version": "2.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/move-file/-/move-file-2.1.0.tgz",
|
||||||
|
"integrity": "sha512-i9qLW6gqboJ5Ht8bauZi7KlTnQ3QFpBCvMvFfEcHADKgHGeJ9BZMO7SFCTwHPV9Qa0du9DYY1Yx3oqlGt30nXA==",
|
||||||
|
"requires": {
|
||||||
|
"path-exists": "^4.0.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"path-exists": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"ms": {
|
"ms": {
|
||||||
"version": "2.1.2",
|
"version": "2.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||||
|
|||||||
@@ -46,6 +46,7 @@
|
|||||||
"knex": "^2.2.0",
|
"knex": "^2.2.0",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"moment": "^2.29.4",
|
"moment": "^2.29.4",
|
||||||
|
"move-file": "^2.1.0",
|
||||||
"rimraf": "^3.0.2",
|
"rimraf": "^3.0.2",
|
||||||
"sails": "^1.5.2",
|
"sails": "^1.5.2",
|
||||||
"sails-hook-orm": "^4.0.1",
|
"sails-hook-orm": "^4.0.1",
|
||||||
|
|||||||
Reference in New Issue
Block a user