-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(user): add profile images (#1041)
* feat(user): add image Also creates static container for core * feat(user): add removing of profile image * fix(image): fix uploading of allowed extensions in caps * chore(user): add tests for image handling * chore(image-tests): upload test images * chore(image-tests): remove mock * chore(image-tests): fix tests
- Loading branch information
1 parent
d384521
commit 7e298d7
Showing
17 changed files
with
1,237 additions
and
50 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,3 +2,4 @@ node_modules/ | |
junit.xml | ||
coverage/ | ||
.seed-executed | ||
tmp_upload/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
worker_processes 4; | ||
pid /run/nginx.pid; | ||
|
||
events { | ||
worker_connections 2048; | ||
multi_accept on; | ||
use epoll; | ||
} | ||
|
||
http { | ||
server_tokens off; | ||
sendfile off; | ||
tcp_nopush on; | ||
tcp_nodelay on; | ||
keepalive_timeout 15; | ||
types_hash_max_size 2048; | ||
client_max_body_size 20M; | ||
include /etc/nginx/mime.types; | ||
default_type application/octet-stream; | ||
access_log /var/log/nginx/access.log; | ||
error_log /var/log/nginx/error.log; | ||
gzip on; | ||
gzip_disable "msie6"; | ||
include /etc/nginx/sites-available/*; | ||
open_file_cache max=100; | ||
charset UTF-8; | ||
|
||
# setting real_ip from Docker network | ||
set_real_ip_from 172.18.0.0/16; | ||
real_ip_header X-Forwarded-For; | ||
real_ip_recursive on; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
server { | ||
listen 80; | ||
server_name oms-frontend; | ||
root "/usr/app/media"; | ||
|
||
charset utf-8; | ||
|
||
location /healthcheck { | ||
alias /usr/app/status.json; | ||
add_header "Content-Type" "application/json"; | ||
} | ||
|
||
location = /favicon.ico { access_log off; log_not_found off; } | ||
location = /robots.txt { access_log off; log_not_found off; } | ||
|
||
access_log /dev/stdout; | ||
error_log stderr; | ||
|
||
sendfile off; | ||
|
||
client_max_body_size 100m; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
{ | ||
"success": true | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
const path = require('path'); | ||
const util = require('util'); | ||
const fs = require('fs'); | ||
const multer = require('multer'); | ||
const readChunk = require('read-chunk'); | ||
const FileType = require('file-type'); | ||
|
||
const errors = require('./errors'); | ||
const log = require('./logger'); | ||
const config = require('../config'); | ||
|
||
const uploadFolderName = `${config.media_dir}/headimages`; | ||
const allowedExtensions = ['.png', '.jpg', '.jpeg']; | ||
|
||
const storage = multer.diskStorage({ // multers disk storage settings | ||
destination(req, file, cb) { | ||
cb(null, uploadFolderName); | ||
}, | ||
|
||
// Filename is 4 character random string and the current datetime to avoid collisions | ||
filename(req, file, cb) { | ||
const prefix = Math.random().toString(36).replace(/[^a-z]+/g, '').substr(0, 4); | ||
const date = (new Date()).getTime(); | ||
const extension = path.extname(file.originalname); | ||
|
||
cb(null, `${prefix}-${date}${extension}`); | ||
}, | ||
}); | ||
const upload = multer({ | ||
storage, | ||
fileFilter(req, file, cb) { | ||
const extension = path.extname(file.originalname).toLowerCase(); | ||
if (!allowedExtensions.includes(extension)) { | ||
const allowed = allowedExtensions.map((e) => `'${e}'`).join(', '); | ||
return cb(new Error(`Allowed extensions: ${allowed}, but '${extension}' was passed.`)); | ||
} | ||
|
||
return cb(null, true); | ||
}, | ||
}).single('head_image'); | ||
|
||
const uploadAsync = util.promisify(upload); | ||
|
||
exports.uploadImage = async (req, res) => { | ||
const oldimg = req.user.image; | ||
|
||
// If upload folder doesn't exists, create it. | ||
if (!fs.existsSync(uploadFolderName)) { | ||
await fs.promises.mkdir(uploadFolderName, { recursive: true }); | ||
} | ||
|
||
try { | ||
await uploadAsync(req, res); | ||
} catch (err) { | ||
log.error({ err }, 'Could not store image'); | ||
return errors.makeValidationError(res, err); | ||
} | ||
|
||
// If the head_image field is missing, do nothing. | ||
if (!req.file) { | ||
return errors.makeValidationError(res, 'No head_image is specified.'); | ||
} | ||
|
||
// If the file's content is malformed, don't save it. | ||
const buffer = readChunk.sync(req.file.path, 0, 4100); | ||
const type = await FileType.fromBuffer(buffer); | ||
|
||
const originalExtension = path.extname(req.file.originalname).toLowerCase(); | ||
const determinedExtension = (type && type.ext ? `.${type.ext}` : 'unknown'); | ||
|
||
if (originalExtension !== determinedExtension || !allowedExtensions.includes(determinedExtension)) { | ||
return errors.makeValidationError(res, 'Malformed file content.'); | ||
} | ||
|
||
await req.user.update({ | ||
image: req.file.filename | ||
}); | ||
|
||
// Remove old file | ||
if (oldimg) { | ||
await fs.promises.unlink(path.join(uploadFolderName, oldimg)); | ||
} | ||
|
||
return res.json({ | ||
success: true, | ||
message: 'File uploaded successfully', | ||
data: req.user.image, | ||
}); | ||
}; | ||
|
||
exports.removeImage = async (req, res) => { | ||
if (!req.user.image) { | ||
return errors.makeValidationError(res, 'No image is specified for the user.'); | ||
} | ||
|
||
await fs.promises.unlink(path.join(uploadFolderName, req.user.image)); | ||
|
||
await req.user.update({ | ||
image: null | ||
}); | ||
|
||
return res.json({ | ||
success: true, | ||
message: 'File removed successfully', | ||
data: req.user.image | ||
}); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
module.exports = { | ||
up: (queryInterface, Sequelize) => queryInterface.addColumn( | ||
'users', | ||
'image', | ||
{ | ||
type: Sequelize.STRING, | ||
allowNull: true | ||
}, | ||
), | ||
down: (queryInterface) => queryInterface.removeColumn('users', 'image') | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.