Save avatars to le cloud in beforeSave hooks

Added encryption to uploads
Updated icon for team settings
This commit is contained in:
Tom Moor
2018-06-01 18:00:48 -04:00
parent a99a804ea0
commit 4c9f86c7f7
7 changed files with 39 additions and 16 deletions

View File

@ -7,6 +7,7 @@ import {
CodeIcon, CodeIcon,
UserIcon, UserIcon,
LinkIcon, LinkIcon,
TeamIcon,
} from 'outline-icons'; } from 'outline-icons';
import Flex from 'shared/components/Flex'; import Flex from 'shared/components/Flex';
@ -55,7 +56,7 @@ class SettingsSidebar extends React.Component<Props> {
<Section> <Section>
<Header>Team</Header> <Header>Team</Header>
{user.isAdmin && ( {user.isAdmin && (
<SidebarLink to="/settings/details" icon={<SettingsIcon />}> <SidebarLink to="/settings/details" icon={<TeamIcon />}>
Details Details
</SidebarLink> </SidebarLink>
)} )}

View File

@ -45,7 +45,7 @@ class DropToImport extends React.Component<Props> {
const asset = await uploadFile(imageBlob, { name: this.file.name }); const asset = await uploadFile(imageBlob, { name: this.file.name });
this.props.onSuccess(asset.url); this.props.onSuccess(asset.url);
} catch (err) { } catch (err) {
this.props.onError(err); this.props.onError(err.message);
} finally { } finally {
this.isUploading = false; this.isUploading = false;
this.isCropping = false; this.isCropping = false;

View File

@ -133,7 +133,7 @@
"nodemailer": "^4.4.0", "nodemailer": "^4.4.0",
"normalize.css": "^7.0.0", "normalize.css": "^7.0.0",
"normalizr": "2.0.1", "normalizr": "2.0.1",
"outline-icons": "^1.1.0", "outline-icons": "^1.2.0",
"oy-vey": "^0.10.0", "oy-vey": "^0.10.0",
"pg": "^6.1.5", "pg": "^6.1.5",
"pg-hstore": "2.3.2", "pg-hstore": "2.3.2",

View File

@ -1,5 +1,7 @@
// @flow // @flow
import uuid from 'uuid';
import { DataTypes, sequelize, Op } from '../sequelize'; import { DataTypes, sequelize, Op } from '../sequelize';
import { publicS3Endpoint, uploadToS3FromUrl } from '../utils/s3';
import Collection from './Collection'; import Collection from './Collection';
import User from './User'; import User from './User';
@ -33,6 +35,18 @@ Team.associate = models => {
Team.hasMany(models.User, { as: 'users' }); Team.hasMany(models.User, { as: 'users' });
}; };
const uploadAvatar = async model => {
const endpoint = publicS3Endpoint();
if (model.avatarUrl && !model.avatarUrl.startsWith(endpoint)) {
const newUrl = await uploadToS3FromUrl(
model.avatarUrl,
`avatars/${model.id}/${uuid.v4()}`
);
if (newUrl) model.avatarUrl = newUrl;
}
};
Team.prototype.createFirstCollection = async function(userId) { Team.prototype.createFirstCollection = async function(userId) {
return await Collection.create({ return await Collection.create({
name: 'General', name: 'General',
@ -82,4 +96,6 @@ Team.prototype.activateUser = async function(user: User, admin: User) {
}); });
}; };
Team.beforeSave(uploadAvatar);
export default Team; export default Team;

View File

@ -4,7 +4,7 @@ import bcrypt from 'bcrypt';
import uuid from 'uuid'; import uuid from 'uuid';
import JWT from 'jsonwebtoken'; import JWT from 'jsonwebtoken';
import { DataTypes, sequelize, encryptedFields } from '../sequelize'; import { DataTypes, sequelize, encryptedFields } from '../sequelize';
import { uploadToS3FromUrl } from '../utils/s3'; import { publicS3Endpoint, uploadToS3FromUrl } from '../utils/s3';
import { sendEmail } from '../mailer'; import { sendEmail } from '../mailer';
const BCRYPT_COST = process.env.NODE_ENV !== 'production' ? 4 : 12; const BCRYPT_COST = process.env.NODE_ENV !== 'production' ? 4 : 12;
@ -57,9 +57,7 @@ User.associate = models => {
User.prototype.getJwtToken = function() { User.prototype.getJwtToken = function() {
return JWT.sign({ id: this.id }, this.jwtSecret); return JWT.sign({ id: this.id }, this.jwtSecret);
}; };
User.prototype.getTeam = async function() {
return this.team;
};
User.prototype.verifyPassword = function(password) { User.prototype.verifyPassword = function(password) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
if (!this.passwordDigest) { if (!this.passwordDigest) {
@ -77,17 +75,23 @@ User.prototype.verifyPassword = function(password) {
}); });
}); });
}; };
User.prototype.updateAvatar = async function() {
this.avatarUrl = await uploadToS3FromUrl( const uploadAvatar = async model => {
this.slackData.image_192, const endpoint = publicS3Endpoint();
`avatars/${this.id}/${uuid.v4()}`
); if (model.avatarUrl && !model.avatarUrl.startsWith(endpoint)) {
const newUrl = await uploadToS3FromUrl(
model.avatarUrl,
`avatars/${model.id}/${uuid.v4()}`
);
if (newUrl) model.avatarUrl = newUrl;
}
}; };
const setRandomJwtSecret = model => { const setRandomJwtSecret = model => {
model.jwtSecret = crypto.randomBytes(64).toString('hex'); model.jwtSecret = crypto.randomBytes(64).toString('hex');
}; };
const hashPassword = function hashPassword(model) { const hashPassword = model => {
if (!model.password) { if (!model.password) {
return null; return null;
} }
@ -106,6 +110,7 @@ const hashPassword = function hashPassword(model) {
}; };
User.beforeCreate(hashPassword); User.beforeCreate(hashPassword);
User.beforeUpdate(hashPassword); User.beforeUpdate(hashPassword);
User.beforeSave(uploadAvatar);
User.beforeCreate(setRandomJwtSecret); User.beforeCreate(setRandomJwtSecret);
User.afterCreate(user => sendEmail('welcome', user.email)); User.afterCreate(user => sendEmail('welcome', user.email));

View File

@ -68,6 +68,7 @@ export const uploadToS3FromUrl = async (url: string, key: string) => {
Key: key, Key: key,
ContentType: res.headers['content-type'], ContentType: res.headers['content-type'],
ContentLength: res.headers['content-length'], ContentLength: res.headers['content-length'],
ServerSideEncryption: 'AES256',
Body: buffer, Body: buffer,
}) })
.promise(); .promise();

View File

@ -7326,9 +7326,9 @@ outline-icons@^1.0.0:
version "1.0.3" version "1.0.3"
resolved "https://registry.yarnpkg.com/outline-icons/-/outline-icons-1.0.3.tgz#f0928a8bbc7e7ff4ea6762eee8fb2995d477941e" resolved "https://registry.yarnpkg.com/outline-icons/-/outline-icons-1.0.3.tgz#f0928a8bbc7e7ff4ea6762eee8fb2995d477941e"
outline-icons@^1.1.0: outline-icons@^1.2.0:
version "1.1.0" version "1.2.0"
resolved "https://registry.yarnpkg.com/outline-icons/-/outline-icons-1.1.0.tgz#08eb188a97a1aa8970a4dded7841c3d8b96b8577" resolved "https://registry.yarnpkg.com/outline-icons/-/outline-icons-1.2.0.tgz#8a0e0e9e9b98336470228837c4933ba10297fcf5"
oy-vey@^0.10.0: oy-vey@^0.10.0:
version "0.10.0" version "0.10.0"