diff --git a/lib/web/imageRouter/azure.js b/lib/web/imageRouter/azure.js deleted file mode 100644 index 22ee5585a..000000000 --- a/lib/web/imageRouter/azure.js +++ /dev/null @@ -1,35 +0,0 @@ -'use strict' -const path = require('path') - -const config = require('../../config') -const logger = require('../../logger') - -const azure = require('azure-storage') - -exports.uploadImage = function (imagePath, callback) { - if (!callback || typeof callback !== 'function') { - logger.error('Callback has to be a function') - return - } - - if (!imagePath || typeof imagePath !== 'string') { - callback(new Error('Image path is missing or wrong'), null) - return - } - - var azureBlobService = azure.createBlobService(config.azure.connectionString) - - azureBlobService.createContainerIfNotExists(config.azure.container, { publicAccessLevel: 'blob' }, function (err, result, response) { - if (err) { - callback(new Error(err.message), null) - } else { - azureBlobService.createBlockBlobFromLocalFile(config.azure.container, path.basename(imagePath), imagePath, function (err, result, response) { - if (err) { - callback(new Error(err.message), null) - } else { - callback(null, azureBlobService.getUrl(config.azure.container, result.name)) - } - }) - } - }) -} diff --git a/lib/web/imageRouter/azure.ts b/lib/web/imageRouter/azure.ts new file mode 100644 index 000000000..9d2f0e1f1 --- /dev/null +++ b/lib/web/imageRouter/azure.ts @@ -0,0 +1,38 @@ +import azure from 'azure-storage' +import path from 'path' + +import { config } from '../../config' +import { logger } from '../../logger' +import { UploadProvider } from './index' + +const AzureUploadProvider: UploadProvider = { + uploadImage: (imagePath, callback) => { + if (!callback || typeof callback !== 'function') { + logger.error('Callback has to be a function') + return + } + + if (!imagePath) { + callback(new Error('Image path is missing or wrong'), undefined) + return + } + + const azureBlobService = azure.createBlobService(config.azure.connectionString) + + azureBlobService.createContainerIfNotExists(config.azure.container, { publicAccessLevel: 'blob' }, function (err, _, __) { + if (err) { + callback(new Error(err.message), undefined) + } else { + azureBlobService.createBlockBlobFromLocalFile(config.azure.container, path.basename(imagePath), imagePath, function (err, result, _) { + if (err) { + callback(new Error(err.message), undefined) + } else { + callback(undefined, azureBlobService.getUrl(config.azure.container, result.name)) + } + }) + } + }) + } +} + +export { AzureUploadProvider } diff --git a/lib/web/imageRouter/filesystem.js b/lib/web/imageRouter/filesystem.js deleted file mode 100644 index 3ba09e88d..000000000 --- a/lib/web/imageRouter/filesystem.js +++ /dev/null @@ -1,20 +0,0 @@ -'use strict' -const URL = require('url').URL -const path = require('path') - -const config = require('../../config') -const logger = require('../../logger') - -exports.uploadImage = function (imagePath, callback) { - if (!callback || typeof callback !== 'function') { - logger.error('Callback has to be a function') - return - } - - if (!imagePath || typeof imagePath !== 'string') { - callback(new Error('Image path is missing or wrong'), null) - return - } - - callback(null, (new URL(path.basename(imagePath), config.serverURL + '/uploads/')).href) -} diff --git a/lib/web/imageRouter/filesystem.ts b/lib/web/imageRouter/filesystem.ts new file mode 100644 index 000000000..79aefa0db --- /dev/null +++ b/lib/web/imageRouter/filesystem.ts @@ -0,0 +1,24 @@ +import path from 'path' +import { URL } from 'url' + +import { config } from '../../config' +import { logger } from '../../logger' +import { UploadProvider } from './index' + +const FilesystemUploadProvider: UploadProvider = { + uploadImage: (imagePath, callback) => { + if (!callback || typeof callback !== 'function') { + logger.error('Callback has to be a function') + return + } + + if (!imagePath) { + callback(new Error('Image path is missing or wrong'), undefined) + return + } + + callback(null, (new URL(path.basename(imagePath), config.serverURL + '/uploads/')).href) + } +} + +export { FilesystemUploadProvider } diff --git a/lib/web/imageRouter/imgur.js b/lib/web/imageRouter/imgur.js deleted file mode 100644 index dcb03a7a5..000000000 --- a/lib/web/imageRouter/imgur.js +++ /dev/null @@ -1,26 +0,0 @@ -'use strict' -const config = require('../../config') -const logger = require('../../logger') - -const imgur = require('imgur') - -exports.uploadImage = function (imagePath, callback) { - if (!callback || typeof callback !== 'function') { - logger.error('Callback has to be a function') - return - } - - if (!imagePath || typeof imagePath !== 'string') { - callback(new Error('Image path is missing or wrong'), null) - return - } - - imgur.setClientId(config.imgur.clientID) - imgur.uploadFile(imagePath) - .then(function (json) { - logger.debug(`SERVER uploadimage success: ${JSON.stringify(json)}`) - callback(null, json.data.link.replace(/^http:\/\//i, 'https://')) - }).catch(function (err) { - callback(new Error(err), null) - }) -} diff --git a/lib/web/imageRouter/imgur.ts b/lib/web/imageRouter/imgur.ts new file mode 100644 index 000000000..b3ae606b1 --- /dev/null +++ b/lib/web/imageRouter/imgur.ts @@ -0,0 +1,30 @@ +import imgur from 'imgur' + +import { config } from '../../config' +import { logger } from '../../logger' +import { UploadProvider } from './index' + +const ImgurUploadProvider: UploadProvider = { + uploadImage: (imagePath, callback) => { + if (!callback || typeof callback !== 'function') { + logger.error('Callback has to be a function') + return + } + + if (!imagePath) { + callback(new Error('Image path is missing or wrong'), undefined) + return + } + + imgur.setClientId(config.imgur.clientID) + imgur.uploadFile(imagePath) + .then(function (json) { + logger.debug(`SERVER uploadimage success: ${JSON.stringify(json)}`) + callback(null, json.data.link.replace(/^http:\/\//i, 'https://')) + }).catch(function (err) { + callback(new Error(err), undefined) + }) + } +} + +export { ImgurUploadProvider } diff --git a/lib/web/imageRouter/index.js b/lib/web/imageRouter/index.js deleted file mode 100644 index aa02e9b09..000000000 --- a/lib/web/imageRouter/index.js +++ /dev/null @@ -1,43 +0,0 @@ -'use strict' - -const Router = require('express').Router -const formidable = require('formidable') - -const config = require('../../config') -const logger = require('../../logger') -const errors = require('../../errors') - -const imageRouter = module.exports = Router() - -// upload image -imageRouter.post('/uploadimage', function (req, res) { - var form = new formidable.IncomingForm() - - form.keepExtensions = true - - if (config.imageUploadType === 'filesystem') { - form.uploadDir = config.uploadsPath - } - - form.parse(req, function (err, fields, files) { - if (err || !files.image || !files.image.path) { - logger.error(`formidable error: ${err}`) - errors.errorForbidden(res) - } else { - logger.debug(`SERVER received uploadimage: ${JSON.stringify(files.image)}`) - - const uploadProvider = require('./' + config.imageUploadType) - logger.debug(`imageRouter: Uploading ${files.image.path} using ${config.imageUploadType}`) - uploadProvider.uploadImage(files.image.path, function (err, url) { - if (err !== null) { - logger.error(err) - return res.status(500).end('upload image error') - } - logger.debug(`SERVER sending ${url} to client`) - res.send({ - link: url - }) - }) - } - }) -}) diff --git a/lib/web/imageRouter/index.ts b/lib/web/imageRouter/index.ts new file mode 100644 index 000000000..7ed46e8d1 --- /dev/null +++ b/lib/web/imageRouter/index.ts @@ -0,0 +1,75 @@ +import { Router } from 'express' +import formidable from 'formidable' + +import { config } from '../../config' +import { logger } from '../../logger' +import { errors } from '../../errors' +import { AzureUploadProvider } from './azure' +import { FilesystemUploadProvider } from './filesystem' +import { ImgurUploadProvider } from './imgur' +import { LutimUploadProvider } from './lutim' +import { MinioUploadProvider } from './minio' +import { S3UploadProvider } from './s3' + +interface UploadProvider { + uploadImage: (imagePath: string, callback: (err?: Error, url?: string) => void) => void; +} + +const ImageRouter = Router() + +// upload image +ImageRouter.post('/uploadimage', function (req, res) { + const form = new formidable.IncomingForm() + + form.keepExtensions = true + + if (config.imageUploadType === 'filesystem') { + form.uploadDir = config.uploadsPath + } + + form.parse(req, function (err, fields, files) { + if (err || !files.image || !files.image.path) { + logger.error(`formidable error: ${err}`) + errors.errorForbidden(res) + } else { + logger.debug(`SERVER received uploadimage: ${JSON.stringify(files.image)}`) + + let uploadProvider: UploadProvider + switch (config.imageUploadType) { + case 'azure': + uploadProvider = AzureUploadProvider + break + case 'filesystem': + default: + uploadProvider = FilesystemUploadProvider + break + case 'imgur': + uploadProvider = ImgurUploadProvider + break + case 'lutim': + uploadProvider = LutimUploadProvider + break + case 'minio': + uploadProvider = MinioUploadProvider + break + case 's3': + uploadProvider = S3UploadProvider + break + } + + logger.debug(`imageRouter: Uploading ${files.image.path} using ${config.imageUploadType}`) + uploadProvider.uploadImage(files.image.path, function (err, url) { + if (err !== undefined) { + logger.error(err) + return res.status(500).end('upload image error') + } + logger.debug(`SERVER sending ${url} to client`) + res.send({ + link: url + }) + }) + } + }) +}) + +export { ImageRouter, UploadProvider } diff --git a/lib/web/imageRouter/lutim.js b/lib/web/imageRouter/lutim.js deleted file mode 100644 index ea13fd9d9..000000000 --- a/lib/web/imageRouter/lutim.js +++ /dev/null @@ -1,30 +0,0 @@ -'use strict' -const config = require('../../config') -const logger = require('../../logger') - -const lutim = require('lutim') - -exports.uploadImage = function (imagePath, callback) { - if (!callback || typeof callback !== 'function') { - logger.error('Callback has to be a function') - return - } - - if (!imagePath || typeof imagePath !== 'string') { - callback(new Error('Image path is missing or wrong'), null) - return - } - - if (config.lutim && config.lutim.url) { - lutim.setAPIUrl(config.lutim.url) - logger.debug(`Set lutim URL to ${lutim.getAPIUrl()}`) - } - - lutim.uploadImage(imagePath) - .then(function (json) { - logger.debug(`SERVER uploadimage success: ${JSON.stringify(json)}`) - callback(null, lutim.getAPIUrl() + json.msg.short) - }).catch(function (err) { - callback(new Error(err), null) - }) -} diff --git a/lib/web/imageRouter/lutim.ts b/lib/web/imageRouter/lutim.ts new file mode 100644 index 000000000..afb388afc --- /dev/null +++ b/lib/web/imageRouter/lutim.ts @@ -0,0 +1,34 @@ +import lutim from 'lutim' + +import { config } from '../../config' +import { logger } from '../../logger' +import { UploadProvider } from './index' + +const LutimUploadProvider: UploadProvider = { + uploadImage: (imagePath, callback) => { + if (!callback || typeof callback !== 'function') { + logger.error('Callback has to be a function') + return + } + + if (!imagePath) { + callback(new Error('Image path is missing or wrong'), undefined) + return + } + + if (config.lutim && config.lutim.url) { + lutim.setAPIUrl(config.lutim.url) + logger.debug(`Set lutim URL to ${lutim.getAPIUrl()}`) + } + + lutim.uploadImage(imagePath) + .then(function (json) { + logger.debug(`SERVER uploadimage success: ${JSON.stringify(json)}`) + callback(undefined, lutim.getAPIUrl() + json.msg.short) + }).catch(function (err) { + callback(new Error(err), undefined) + }) + } +} + +export { LutimUploadProvider } diff --git a/lib/web/imageRouter/minio.js b/lib/web/imageRouter/minio.js deleted file mode 100644 index fe43f76ff..000000000 --- a/lib/web/imageRouter/minio.js +++ /dev/null @@ -1,48 +0,0 @@ -'use strict' -const fs = require('fs') -const path = require('path') - -const config = require('../../config') -const { getImageMimeType } = require('../../utils') -const logger = require('../../logger') - -const Minio = require('minio') -const minioClient = new Minio.Client({ - endPoint: config.minio.endPoint, - port: config.minio.port, - secure: config.minio.secure, - accessKey: config.minio.accessKey, - secretKey: config.minio.secretKey -}) - -exports.uploadImage = function (imagePath, callback) { - if (!imagePath || typeof imagePath !== 'string') { - callback(new Error('Image path is missing or wrong'), null) - return - } - - if (!callback || typeof callback !== 'function') { - logger.error('Callback has to be a function') - return - } - - fs.readFile(imagePath, function (err, buffer) { - if (err) { - callback(new Error(err), null) - return - } - - let key = path.join('uploads', path.basename(imagePath)) - let protocol = config.minio.secure ? 'https' : 'http' - - minioClient.putObject(config.s3bucket, key, buffer, buffer.size, getImageMimeType(imagePath), function (err, data) { - if (err) { - callback(new Error(err), null) - return - } - let hidePort = [80, 443].includes(config.minio.port) - let urlPort = hidePort ? '' : `:${config.minio.port}` - callback(null, `${protocol}://${config.minio.endPoint}${urlPort}/${config.s3bucket}/${key}`) - }) - }) -} diff --git a/lib/web/imageRouter/minio.ts b/lib/web/imageRouter/minio.ts new file mode 100644 index 000000000..a0b12b394 --- /dev/null +++ b/lib/web/imageRouter/minio.ts @@ -0,0 +1,52 @@ +import path from 'path' +import fs from 'fs' +import Minio from 'minio' + +import { config } from '../../config' +import { getImageMimeType } from '../../utils' +import { logger } from '../../logger' +import { UploadProvider } from './index' + +const minioClient = new Minio.Client({ + endPoint: config.minio.endPoint, + port: config.minio.port, + useSSL: config.minio.secure, + accessKey: config.minio.accessKey, + secretKey: config.minio.secretKey +}) + +const MinioUploadProvider: UploadProvider = { + uploadImage: (imagePath, callback) => { + if (!imagePath) { + callback(new Error('Image path is missing or wrong'), undefined) + return + } + + if (!callback || typeof callback !== 'function') { + logger.error('Callback has to be a function') + return + } + + fs.readFile(imagePath, function (err, buffer) { + if (err) { + callback(new Error(err.message), undefined) + return + } + + const key = path.join('uploads', path.basename(imagePath)) + const protocol = config.minio.secure ? 'https' : 'http' + + minioClient.putObject(config.s3bucket, key, buffer, buffer.length, getImageMimeType(imagePath), function (err, _) { + if (err) { + callback(new Error(err.message), undefined) + return + } + const hidePort = [80, 443].includes(config.minio.port) + const urlPort = hidePort ? '' : `:${config.minio.port}` + callback(undefined, `${protocol}://${config.minio.endPoint}${urlPort}/${config.s3bucket}/${key}`) + }) + }) + } +} + +export { MinioUploadProvider } diff --git a/lib/web/imageRouter/s3.js b/lib/web/imageRouter/s3.js deleted file mode 100644 index 2bf08cc7b..000000000 --- a/lib/web/imageRouter/s3.js +++ /dev/null @@ -1,54 +0,0 @@ -'use strict' -const fs = require('fs') -const path = require('path') - -const config = require('../../config') -const { getImageMimeType } = require('../../utils') -const logger = require('../../logger') - -const AWS = require('aws-sdk') -const awsConfig = new AWS.Config(config.s3) -const s3 = new AWS.S3(awsConfig) - -exports.uploadImage = function (imagePath, callback) { - if (!imagePath || typeof imagePath !== 'string') { - callback(new Error('Image path is missing or wrong'), null) - return - } - - if (!callback || typeof callback !== 'function') { - logger.error('Callback has to be a function') - return - } - - fs.readFile(imagePath, function (err, buffer) { - if (err) { - callback(new Error(err), null) - return - } - let params = { - Bucket: config.s3bucket, - Key: path.join('uploads', path.basename(imagePath)), - Body: buffer - } - - const mimeType = getImageMimeType(imagePath) - if (mimeType) { params.ContentType = mimeType } - - logger.debug(`S3 object parameters: ${JSON.stringify(params)}`) - s3.putObject(params, function (err, data) { - if (err) { - callback(new Error(err), null) - return - } - - let s3Endpoint = 's3.amazonaws.com' - if (config.s3.endpoint) { - s3Endpoint = config.s3.endpoint - } else if (config.s3.region && config.s3.region !== 'us-east-1') { - s3Endpoint = `s3-${config.s3.region}.amazonaws.com` - } - callback(null, `https://${s3Endpoint}/${config.s3bucket}/${params.Key}`) - }) - }) -} diff --git a/lib/web/imageRouter/s3.ts b/lib/web/imageRouter/s3.ts new file mode 100644 index 000000000..4c58ff488 --- /dev/null +++ b/lib/web/imageRouter/s3.ts @@ -0,0 +1,59 @@ +import fs from 'fs' +import path from 'path' +import AWS from 'aws-sdk' + +import { config } from '../../config' +// import { getImageMimeType } from '../../utils' +import { logger } from '../../logger' +import { UploadProvider } from './index' + +const awsConfig = new AWS.Config(config.s3) +const s3 = new AWS.S3(awsConfig) + +const S3UploadProvider: UploadProvider = { + uploadImage: (imagePath, callback) => { + if (!imagePath) { + callback(new Error('Image path is missing or wrong'), undefined) + return + } + + if (!callback || typeof callback !== 'function') { + logger.error('Callback has to be a function') + return + } + + fs.readFile(imagePath, function (err, buffer) { + if (err) { + callback(new Error(err.message), undefined) + return + } + const params = { + Bucket: config.s3bucket, + Key: path.join('uploads', path.basename(imagePath)), + Body: buffer + } + + // ToDo: This does not exist (anymore?) + // const mimeType = getImageMimeType(imagePath) + // if (mimeType) { params.ContentType = mimeType } + + logger.debug(`S3 object parameters: ${JSON.stringify(params)}`) + s3.putObject(params, function (err, _) { + if (err) { + callback(new Error(err.message), undefined) + return + } + + let s3Endpoint = 's3.amazonaws.com' + if (config.s3.endpoint) { + s3Endpoint = config.s3.endpoint + } else if (config.s3.region && config.s3.region !== 'us-east-1') { + s3Endpoint = `s3-${config.s3.region}.amazonaws.com` + } + callback(undefined, `https://${s3Endpoint}/${config.s3bucket}/${params.Key}`) + }) + }) + } +} + +export { S3UploadProvider }