diff --git a/app/components/Sidebar/Settings.js b/app/components/Sidebar/Settings.js index d9863100e..60af185ad 100644 --- a/app/components/Sidebar/Settings.js +++ b/app/components/Sidebar/Settings.js @@ -7,6 +7,7 @@ import { CodeIcon, UserIcon, LinkIcon, + TeamIcon, } from 'outline-icons'; import Flex from 'shared/components/Flex'; @@ -55,7 +56,7 @@ class SettingsSidebar extends React.Component {
Team
{user.isAdmin && ( - }> + }> Details )} diff --git a/app/scenes/Settings/components/ImageUpload.js b/app/scenes/Settings/components/ImageUpload.js index 6c4522719..e0aed52af 100644 --- a/app/scenes/Settings/components/ImageUpload.js +++ b/app/scenes/Settings/components/ImageUpload.js @@ -45,7 +45,7 @@ class DropToImport extends React.Component { const asset = await uploadFile(imageBlob, { name: this.file.name }); this.props.onSuccess(asset.url); } catch (err) { - this.props.onError(err); + this.props.onError(err.message); } finally { this.isUploading = false; this.isCropping = false; diff --git a/package.json b/package.json index 40ef2e469..d9e381573 100644 --- a/package.json +++ b/package.json @@ -133,7 +133,7 @@ "nodemailer": "^4.4.0", "normalize.css": "^7.0.0", "normalizr": "2.0.1", - "outline-icons": "^1.1.0", + "outline-icons": "^1.2.0", "oy-vey": "^0.10.0", "pg": "^6.1.5", "pg-hstore": "2.3.2", diff --git a/server/models/Team.js b/server/models/Team.js index 1ce90d28b..808e85b5b 100644 --- a/server/models/Team.js +++ b/server/models/Team.js @@ -1,5 +1,7 @@ // @flow +import uuid from 'uuid'; import { DataTypes, sequelize, Op } from '../sequelize'; +import { publicS3Endpoint, uploadToS3FromUrl } from '../utils/s3'; import Collection from './Collection'; import User from './User'; @@ -33,6 +35,18 @@ Team.associate = models => { 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) { return await Collection.create({ name: 'General', @@ -82,4 +96,6 @@ Team.prototype.activateUser = async function(user: User, admin: User) { }); }; +Team.beforeSave(uploadAvatar); + export default Team; diff --git a/server/models/User.js b/server/models/User.js index 4c6e68ce9..117b33405 100644 --- a/server/models/User.js +++ b/server/models/User.js @@ -4,7 +4,7 @@ import bcrypt from 'bcrypt'; import uuid from 'uuid'; import JWT from 'jsonwebtoken'; import { DataTypes, sequelize, encryptedFields } from '../sequelize'; -import { uploadToS3FromUrl } from '../utils/s3'; +import { publicS3Endpoint, uploadToS3FromUrl } from '../utils/s3'; import { sendEmail } from '../mailer'; const BCRYPT_COST = process.env.NODE_ENV !== 'production' ? 4 : 12; @@ -57,9 +57,7 @@ User.associate = models => { User.prototype.getJwtToken = function() { return JWT.sign({ id: this.id }, this.jwtSecret); }; -User.prototype.getTeam = async function() { - return this.team; -}; + User.prototype.verifyPassword = function(password) { return new Promise((resolve, reject) => { if (!this.passwordDigest) { @@ -77,17 +75,23 @@ User.prototype.verifyPassword = function(password) { }); }); }; -User.prototype.updateAvatar = async function() { - this.avatarUrl = await uploadToS3FromUrl( - this.slackData.image_192, - `avatars/${this.id}/${uuid.v4()}` - ); + +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; + } }; const setRandomJwtSecret = model => { model.jwtSecret = crypto.randomBytes(64).toString('hex'); }; -const hashPassword = function hashPassword(model) { +const hashPassword = model => { if (!model.password) { return null; } @@ -106,6 +110,7 @@ const hashPassword = function hashPassword(model) { }; User.beforeCreate(hashPassword); User.beforeUpdate(hashPassword); +User.beforeSave(uploadAvatar); User.beforeCreate(setRandomJwtSecret); User.afterCreate(user => sendEmail('welcome', user.email)); diff --git a/server/utils/s3.js b/server/utils/s3.js index c8d0c08e1..7edeb384d 100644 --- a/server/utils/s3.js +++ b/server/utils/s3.js @@ -68,6 +68,7 @@ export const uploadToS3FromUrl = async (url: string, key: string) => { Key: key, ContentType: res.headers['content-type'], ContentLength: res.headers['content-length'], + ServerSideEncryption: 'AES256', Body: buffer, }) .promise(); diff --git a/yarn.lock b/yarn.lock index b7175aba8..cb46e1b43 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7326,9 +7326,9 @@ outline-icons@^1.0.0: version "1.0.3" resolved "https://registry.yarnpkg.com/outline-icons/-/outline-icons-1.0.3.tgz#f0928a8bbc7e7ff4ea6762eee8fb2995d477941e" -outline-icons@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/outline-icons/-/outline-icons-1.1.0.tgz#08eb188a97a1aa8970a4dded7841c3d8b96b8577" +outline-icons@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/outline-icons/-/outline-icons-1.2.0.tgz#8a0e0e9e9b98336470228837c4933ba10297fcf5" oy-vey@^0.10.0: version "0.10.0"