diff --git a/Dockerfile b/Dockerfile index d9f4608..4662859 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,15 +3,15 @@ MAINTAINER "Mahesh Kumar Gangula" "mahesh@ilimi.in" USER root COPY src /opt/print-service/ WORKDIR /opt/print-service/ -ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD true +#ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD true RUN npm install --unsafe-perm -FROM node:8.11-slim +FROM node:node:8-buster-slim MAINTAINER "Mahesh Kumar Gangula" "mahesh@ilimi.in" RUN apt-get clean \ && wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add - \ && sh -c 'echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google.list' \ && apt update && apt install fonts-indic -y \ - && apt-get install -y google-chrome-unstable \ + && apt-get install -y google-chrome-unstable gconf-service libasound2 libatk1.0-0 libc6 libcairo2 libcups2 libdbus-1-3 libexpat1 libfontconfig1 libgcc1 libgconf-2-4 libgdk-pixbuf2.0-0 libglib2.0-0 libgtk-3-0 libnspr4 libpango-1.0-0 libpangocairo-1.0-0 libstdc++6 libx11-6 libx11-xcb1 libxcb1 libxcomposite1 libxcursor1 libxdamage1 libxext6 libxfixes3 libxi6 libxrandr2 libxrender1 libxss1 libxtst6 ca-certificates fonts-liberation libappindicator1 libnss3 lsb-release xdg-utils wget \ --no-install-recommends \ && rm -rf /var/lib/apt/lists/* RUN groupadd -r sunbird && useradd -r -g sunbird -G audio,video sunbird \ @@ -21,4 +21,7 @@ RUN fc-cache -f -v USER sunbird COPY --from=build --chown=sunbird /opt/print-service/ /home/sunbird/print-service/ WORKDIR /home/sunbird/print-service/ -CMD ["node", "app.js", "&"] +# All the downloaded zip will be present inside certs folder. +RUN mkdir /home/sunbird/print-service/certs +ENV NODE_ENV production +CMD ["node","app.js","&"] diff --git a/auto_build_deploy b/auto_build_deploy new file mode 100644 index 0000000..1eea026 --- /dev/null +++ b/auto_build_deploy @@ -0,0 +1,53 @@ +@Library('deploy-conf') _ +node('build-slave') { + try { + String ANSI_GREEN = "\u001B[32m" + String ANSI_NORMAL = "\u001B[0m" + String ANSI_BOLD = "\u001B[1m" + String ANSI_RED = "\u001B[31m" + String ANSI_YELLOW = "\u001B[33m" + + ansiColor('xterm') { + stage('Checkout') { + tag_name = env.JOB_NAME.split("/")[-1] + pre_checks() + if (!env.hub_org) { + println(ANSI_BOLD + ANSI_RED + "Uh Oh! Please set a Jenkins environment variable named hub_org with value as registery/sunbidrded" + ANSI_NORMAL) + error 'Please resolve the errors and rerun..' + } + else + println(ANSI_BOLD + ANSI_GREEN + "Found environment variable named hub_org with value as: " + hub_org + ANSI_NORMAL) + cleanWs() + def scmVars = checkout scm + checkout scm: [$class: 'GitSCM', branches: [[name: "refs/tags/$tag_name"]], userRemoteConfigs: [[url: scmVars.GIT_URL]]] + build_tag = tag_name + "_" + env.BUILD_NUMBER + commit_hash = sh(script: 'git rev-parse --short HEAD', returnStdout: true).trim() + artifact_version = tag_name + "_" + commit_hash + echo "build_tag: " + build_tag + } + + // stage Build + env.NODE_ENV = "build" + print "Environment will be : ${env.NODE_ENV}" + sh('chmod 777 build.sh') + sh("./build.sh ${build_tag} ${env.NODE_NAME} ${hub_org}") + + // stage ArchiveArtifacts + archiveArtifacts "metadata.json" + currentBuild.description = "${build_tag}" + + } + currentBuild.result = "SUCCESS" + slack_notify(currentBuild.result, tag_name) + email_notify() + auto_build_deploy() + + } + catch (err) { + currentBuild.result = "FAILURE" + slack_notify(currentBuild.result, tag_name) + email_notify() + throw err + } + +} diff --git a/src/FileManager.js b/src/FileManager.js new file mode 100644 index 0000000..bfceaa1 --- /dev/null +++ b/src/FileManager.js @@ -0,0 +1,30 @@ +const fs = require('fs') +const logger = require('./sdk/log4js'); + + + +const getAbsolutePath = (path) => { + var fullpath = __dirname +"/"+path; + console.log("absolute path of download zip is:",fullpath ) + return fullpath; +} + + + +const deleteFiles = (filePaths)=>{ +filePaths.forEach(element => { + fs.unlink(element, deleteFile) +});} + + +var deleteFile= function (err) { + if (err) { + logger.info("unlink failed", err); + } else { + logger.info("file deleted"); + } +} + + +exports.getAbsolutePath= getAbsolutePath; +exports.deleteFiles = deleteFiles; diff --git a/src/app.js b/src/app.js index 36ecc06..fadac18 100644 --- a/src/app.js +++ b/src/app.js @@ -1,7 +1,7 @@ const express = require('express'), cluster = require('express-cluster'), cookieParser = require('cookie-parser'), - logger = require('morgan'), + logger = require('./sdk/log4js'), bodyParser = require('body-parser'), envVariables = require('./envVariables'), port = envVariables.port, @@ -17,7 +17,6 @@ const express = require('express'), else next() }) app.use(bodyParser.json({ limit: '1mb' })); - app.use(logger('dev')); app.use(express.json()); app.use(bodyParser.urlencoded({ extended: false })); app.use(cookieParser()); @@ -32,7 +31,6 @@ const express = require('express'), return app.listen(port, () => console.log(`print-service cluster is running on port ${port} with ${process.pid} pid`)); }, { count: threads }); } else { - const app = createAppServer(); + const app = createAppServer(); app.listen(port, () => console.log(`print-service is running in test env on port ${port} with ${process.pid} pid`)); - } - \ No newline at end of file + } \ No newline at end of file diff --git a/src/envVariables.js b/src/envVariables.js index e120738..80a80de 100644 --- a/src/envVariables.js +++ b/src/envVariables.js @@ -1,13 +1,18 @@ const os = require('os'); const envVariables = { - port: process.env.service_port || 5000, - threads: process.env.service_threads || os.cpus().length, - azureAccountName: process.env.sunbird_azure_account_name, - azureAccountKey: process.env.sunbird_azure_account_key, - azureContainerName: process.env.sunbird_azure_container_name, - level: process.env.service_log_level || 'info', - encodingType: process.env.service_encoding_type, - filename: process.env.service_file_filename || 'print-service-%DATE%.log' + port: process.env.service_port || 5000, + threads: process.env.service_threads || os.cpus().length, + azureAccountName: process.env.sunbird_azure_account_name, + azureAccountKey: process.env.sunbird_azure_account_key, + azureContainerName: process.env.sunbird_azure_container_name, + privateContainer: { + azureAccountName: process.env.sunbird_pvt_azure_account_name, + azureAccountKey: process.env.sunbird_pvt_azure_account_key, + azureContainerName: process.env.sunbird_pvt_azure_container_name + }, + level: process.env.service_log_level || 'info', + encodingType: process.env.service_encoding_type, + filename: process.env.service_file_filename || 'print-service-%DATE%.log', } module.exports = envVariables; \ No newline at end of file diff --git a/src/generators/HtmlGenerator.js b/src/generators/HtmlGenerator.js new file mode 100644 index 0000000..103ae0f --- /dev/null +++ b/src/generators/HtmlGenerator.js @@ -0,0 +1,36 @@ +const fs = require('fs'); +const Mapper = require('./Mapper') +const encodingType = 'utf8'; +const logger = require('../sdk/log4js'); + + +/** + * @author Anmol Gupta + */ +class HtmlGenerator{ + + constructor(htmlFilePath, request){ + this.htmlFilePath=htmlFilePath; + this.request= request; + } + + + generateTempHtmlFile(){ + var startTime = Date.now(); + var htmlFile = fs.readFileSync(this.htmlFilePath,encodingType); + var mapper = new Mapper(htmlFile, this.request.getContextMap()); + var mappedHtml = mapper.replacePlaceholders(); + var mappedHtmlFilePath = this.getReqIdHtmlFilePath(); + fs.writeFileSync(mappedHtmlFilePath,mappedHtml) + logger.debug("HtmlGenerator:generateTempHtmlFile:file written successfully in ms:", Date.now()-startTime) + return mappedHtmlFilePath; + } + + getReqIdHtmlFilePath(){ + var tempHtmlFilePath = this.htmlFilePath.replace("index.html", this.request.getRequestId()+".html") + logger.info("HtmlGenerator:getReqIdHtmlFilePath:the temp filepath formed is", tempHtmlFilePath) + return tempHtmlFilePath; + } + +} +module.exports = HtmlGenerator; \ No newline at end of file diff --git a/src/generators/Mapper.js b/src/generators/Mapper.js new file mode 100644 index 0000000..b3397cc --- /dev/null +++ b/src/generators/Mapper.js @@ -0,0 +1,23 @@ +var velocity = require('velocityjs'); + +/** + * @author Anmol Gupta + */ + + +class Mapper{ + + constructor(htmlFile,contextMap) + { + this.htmlFile= htmlFile; + this.contextMap= contextMap; + } + + + replacePlaceholders(){ + var asts = velocity.parse(this.htmlFile); + var results = (new velocity.Compile(asts)).render(this.contextMap,null); + return results; + } +} +module.exports= Mapper; \ No newline at end of file diff --git a/src/helpers/DownloadManager.js b/src/helpers/DownloadManager.js new file mode 100644 index 0000000..d7f8583 --- /dev/null +++ b/src/helpers/DownloadManager.js @@ -0,0 +1,30 @@ +const logger = require('../sdk/log4js'); +const request = require('superagent'); +const fs = require('fs'); + + +/** + * @author Anmol Gupta + */ +class DownloadManager { + + constructor(downloadParams) { + this.downloadParams = downloadParams; + } + + + downloadFile(callback) { + request + .get(this.downloadParams.getSourceUrl()) + .on('error', function (error) { + logger.error("DownloadManager:downloadFile:",error); + }) + .pipe(fs.createWriteStream(this.downloadParams.getDownloadPath())) + .on('finish', function () { + logger.info('finished dowloading'); + callback(null) + }); + } +} + +module.exports = DownloadManager \ No newline at end of file diff --git a/src/helpers/DownloadParams.js b/src/helpers/DownloadParams.js new file mode 100644 index 0000000..32be084 --- /dev/null +++ b/src/helpers/DownloadParams.js @@ -0,0 +1,48 @@ +const logger = require('../sdk/log4js'); + +/** + * @author Anmol Gupta + */ + +class DownloadParams { + + + constructor(sourceUrl) { + this.sourceUrl = sourceUrl; + this.CERT_DOWNLOAD_FOLDER= "certs/" + } + + getDownloadPath() { + const fileName = this.getFileName() + const downloadPath = this.CERT_DOWNLOAD_FOLDER.concat(fileName) + logger.info("DownloadParams:getDownloadPath:Download path formed:", downloadPath) + return downloadPath; + } + + + getSourceUrl() { + return this.sourceUrl; + } + + getFileExtractToPath() { + const extactToPath = this.CERT_DOWNLOAD_FOLDER.concat(this.getFileName().replace(".zip", "/")) + logger.info("DownloadParams:getFileExtractToPath got:", extactToPath) + return extactToPath + } + + + getHtmlPath() { + const htmlPath = this.CERT_DOWNLOAD_FOLDER.concat(this.getFileName().replace(".zip", "/index.html")) + logger.info("DownloadParams:getHtmlPath:index.html file path is:", htmlPath) + return htmlPath; + + + } + + getFileName() { + return this.sourceUrl.substring(this.sourceUrl.lastIndexOf('/') + 1); + } + +} + +module.exports = DownloadParams; \ No newline at end of file diff --git a/src/helpers/FileExtractor.js b/src/helpers/FileExtractor.js new file mode 100644 index 0000000..9263b17 --- /dev/null +++ b/src/helpers/FileExtractor.js @@ -0,0 +1,30 @@ +const logger = require('../sdk/log4js'); +const admZip = require('adm-zip'); +const filemanager = require('../FileManager') + + +/** + * @author Anmol Gupta + */ + + +class FileExtactor { + + constructor(downloadParams) { + this.downloadParams = downloadParams + } +/** + * this method will extract the zip file and return the absolute path file uri. + * @param {*} callback + */ + extractZipFile(callback) { + const startTime = Date.now(); + var zip = new admZip(this.downloadParams.getDownloadPath()); + logger.info('FileExtractor:extractZipFile:start unzip at path', this.downloadParams.getFileExtractToPath()); + zip.extractAllTo(this.downloadParams.getFileExtractToPath(), true); + logger.debug('FileExtractor:extractZipFile:finished unzip in secs:', Date.now() - startTime); + callback(null,filemanager.getAbsolutePath(this.downloadParams.getHtmlPath())) + } +} + +module.exports = FileExtactor; \ No newline at end of file diff --git a/src/helpers/Request.js b/src/helpers/Request.js new file mode 100644 index 0000000..9c15ec3 --- /dev/null +++ b/src/helpers/Request.js @@ -0,0 +1,37 @@ + + + + +/** + * @author Anmol Gupta + */ + + + +class Request { + + constructor(contextMap, htmlTemplate, requestId, storageParmas) { + this.contextMap = contextMap; + this.htmlTemplate = htmlTemplate; + this.requestId = requestId; + this.storageParmas= storageParmas; + } + + getContextMap() { + return this.contextMap; + } + + getStorageParams(){ + return this.storageParmas; + } + + getRequestId() { + return this.requestId; + } + getHtmlTemplate(){ + return this.htmlTemplate; + } + +} + +module.exports = Request; \ No newline at end of file diff --git a/src/helpers/StorageParams.js b/src/helpers/StorageParams.js new file mode 100644 index 0000000..c8468dd --- /dev/null +++ b/src/helpers/StorageParams.js @@ -0,0 +1,31 @@ + +/** + * @author Anmol Gupta + */ + +class StorageParams{ + +constructor(){ + +} + + +setPath(path){ + this.path = path; +} + +setContainerName(containerName){ + this.containerName= containerName; +} + +getPath(){ + return this.path; +} + +getContainerName(){ + return this.containerName; +} + +} + +module.exports=StorageParams; \ No newline at end of file diff --git a/src/helpers/TemplateProcessor.js b/src/helpers/TemplateProcessor.js new file mode 100644 index 0000000..7efb4f2 --- /dev/null +++ b/src/helpers/TemplateProcessor.js @@ -0,0 +1,65 @@ +const logger = require('../sdk/log4js'); +var async = require("async"); +var FileExtactor = require('./FileExtractor') +var DownloadManager = require('./DownloadManager') +const filemanager = require('../FileManager') +const fs = require('fs') + + + +/** + * @author Anmol Gupta + */ + + +class TemplateProcessor { + + constructor(downloadParams) { + this.downloadParams = downloadParams; + } + + processTemplate() { + var downloadManager = new DownloadManager(this.downloadParams) + var fileExtractor = new FileExtactor(this.downloadParams) + var htmlAbsFilePath = filemanager.getAbsolutePath(this.downloadParams.getHtmlPath()) + var fileCheckResult = this.checkFileExists(htmlAbsFilePath) + if(!fileCheckResult) { + return new Promise(function (resolve, reject) { + async.waterfall([ + (callback) => { + downloadManager.downloadFile(callback); + }, + (callback2) => { + fileExtractor.extractZipFile(callback2) + } + + ], (err, result) => { + if (!err) { + logger.info("TemplateProcessor:processTemplate:index.html file absolute path:", result) + resolve(result) + } + else { + logger.error("TemplateProcessor:processTemplate:index.html error occurred ",err); + throw (err) + } + }); + }) + } + else { + return new Promise(function (resolve, reject) { + resolve(htmlAbsFilePath); + }) + } + } + + checkFileExists(htmlAbsFilePath) { + if (fs.existsSync(htmlAbsFilePath)) { + logger.info('TemplateProcessor:checkFileExixsts:Found file in cache skip downloading..'); + return true; + } + logger.info('TemplateProcessor:checkFileExixsts:NO Found file in cache downloading..'); + return false; + } +} + +module.exports = TemplateProcessor \ No newline at end of file diff --git a/src/helpers/constants.js b/src/helpers/constants.js index c6270ae..57d3b83 100644 --- a/src/helpers/constants.js +++ b/src/helpers/constants.js @@ -3,21 +3,36 @@ module.exports.apiIds = Object.freeze({ PRINT_API_ID: 'api.print.preview.generate', HEALTH_API_ID: 'api.health', }) -module.exports.responseCodes = Object.freeze({ +module.exports.responseCodes = Object.freeze({ SUCCESS: { - name: 'OK', + name: 'OK', code: 200 }, CLIENT_ERROR: { - name: 'CLIENT_ERROR', + name: 'CLIENT_ERROR', code: 400 }, SERVER_ERROR: { - name: 'SERVER_ERROR', + name: 'SERVER_ERROR', code: 500 }, NOT_FOUND: { - name: 'RESOURCE_NOT_FOUND', + name: 'RESOURCE_NOT_FOUND', code: 404 + } +}) + + +module.exports.argsConfig = { + + PROD_MODE: { + executablePath: 'google-chrome-unstable', + args: ['--disable-dev-shm-usage', '--no-sandbox', '--disable-setuid-sandbox'] }, -}) \ No newline at end of file + + DEBUG_MODE: + { + args: ['--disable-dev-shm-usage', '--no-sandbox', '--disable-setuid-sandbox'] + } + +} diff --git a/src/package.json b/src/package.json index 33b359e..4784c99 100644 --- a/src/package.json +++ b/src/package.json @@ -4,16 +4,22 @@ "description": "", "main": "index.js", "dependencies": { + "adm-zip": "^0.4.14", + "async": "^3.2.0", "azure-storage": "^2.7.0", "body-parser": "~1.18.3", "cookie-parser": "~1.4.3", "express": "~4.16.0", "express-cluster": "0.0.5", "http-errors": "~1.6.2", - "morgan": "~1.9.0", + "log4js": "^4.0.2", "puppeteer": "2.0.0", + "puppeteer-cluster": "^0.21.0", "request": "2.87.0", - "uuid": "~3.2.1" + "superagent": "^5.2.2", + "util": "^0.12.2", + "uuid": "~3.2.1", + "velocityjs": "^2.0.0" }, "devDependencies": {}, "scripts": { diff --git a/src/routes/index.js b/src/routes/index.js index 4dd5f17..67d5270 100644 --- a/src/routes/index.js +++ b/src/routes/index.js @@ -1,9 +1,18 @@ const express = require('express'), router = express.Router(), printService = require('../service/print-service'); - + const setConnectionTimeout = (time) => { + return (req, res, next) => { + req.connection.setTimeout(time); + next(); + }; + } + router.post('/v1/print/preview/generate', (req, res) => printService.generate(req, res)); +router.post('/v1/print/pdf',setConnectionTimeout(120000), (req, res) => printService.printPdf(req, res)); + router.get('/health', (req, res) => printService.health(req, res)); module.exports = router; + diff --git a/src/sdk/log4js.js b/src/sdk/log4js.js new file mode 100644 index 0000000..b78e2a9 --- /dev/null +++ b/src/sdk/log4js.js @@ -0,0 +1,17 @@ +const log4js = require('log4js'); +log4js.configure({ + appenders: { + consoleAppender: { + type: 'console', layout: { + type: 'pattern', pattern: '%d %[%p%] %c %f{1}:%l %m' + } + } + }, + categories: { + default: { + appenders: ['consoleAppender'], level: 'info', enableCallStack: true + }, + }, +}); +const logger = log4js.getLogger('print-service'); +module.exports = logger; \ No newline at end of file diff --git a/src/service/print-service.js b/src/service/print-service.js index a7774e8..a01aad7 100644 --- a/src/service/print-service.js +++ b/src/service/print-service.js @@ -1,70 +1,178 @@ +const logger = require('../sdk/log4js'); const uuidv1 = require('uuid/v1'), request = require('request'), puppeteer = require('puppeteer'), azure = require('azure-storage'), path = require('path'), config = require('../envVariables'), - constants = require('../helpers/constants') - - - + constants = require('../helpers/constants'), + DownloadParams = require('../helpers/DownloadParams'), + TemplateProcessor = require('../helpers/TemplateProcessor'), + Request = require('../helpers/Request'), + HtmlGenerator = require('../generators/HtmlGenerator'), + filemanager = require('../FileManager'), + serviceName = 'print-service/', + StorageParams = require('../helpers/StorageParams'), + util = require('util'); +const { Cluster } = require('puppeteer-cluster'); class PrintService { constructor(config) { - (async() => { + (async () => { try { - this.config = config; - this.pdfBasePath = '/tmp/' - this.browser = await puppeteer.launch({ - executablePath: 'google-chrome-unstable', - args: ['--disable-dev-shm-usage', '--no-sandbox', '--disable-setuid-sandbox'] + this.config = config; + this.pdfBasePath = '/tmp/' + this.blobService = azure.createBlobService(this.config.azureAccountName, this.config.azureAccountKey); + this.pvtBlobService = azure.createBlobService(this.config.privateContainer.azureAccountName, this.config.privateContainer.azureAccountKey); + // Create a cluster with 10 workers + this.puppeteerCluster = await Cluster.launch({ + concurrency: Cluster.CONCURRENCY_PAGE, + maxConcurrency: 10 + }); + // Define a task + await this.puppeteerCluster.task(async ({ page, data }) => { + if(data.taskType === 'pdf'){ + page.setDefaultNavigationTimeout(0); + await page.goto(data.src); + await page.pdf({ + path: data.dest, format: 'A4', printBackground: true + }); + } + console.log('in cluster task generated pdf for pdfFilePath', data.dest); + return true }); - this.blobService = azure.createBlobService(this.config.azureAccountName, this.config.azureAccountKey); - } catch(e) { - console.error(e); + } catch (e) { + logger.error("error while launching puppeteer", e) } - })(); } - + printPdf(req, res) { + (async () => { + try { + this.validateRequest(res, req.body.request) + var request = this.getComposedRequest(req.body.request); + var dowloadParams = new DownloadParams(request.getHtmlTemplate()) + var templateProcessor = new TemplateProcessor(dowloadParams) + var dataPromise = templateProcessor.processTemplate() + dataPromise.then(async htmlFilePath => { + try { + logger.info("PrintService:printPdg:the index html file got:", htmlFilePath) + var htmlGenerator = new HtmlGenerator(htmlFilePath, request); + var mappedHtmlFilePath = htmlGenerator.generateTempHtmlFile() + var args = constants.argsConfig.DEBUG_MODE + if (!this.detectDebug()) { + args = constants.argsConfig.PROD_MODE + } + const pdfFilePath = filemanager.getAbsolutePath(dowloadParams.getFileExtractToPath()) + request.getRequestId() + '.pdf'; + await this.puppeteerCluster.execute({ + taskType: 'pdf', + src: "file://" + mappedHtmlFilePath, + dest: pdfFilePath + }); + console.log('in printPdf generated pdf for pdfFilePath', pdfFilePath); + const destPath = request.getStorageParams().getPath() + path.basename(pdfFilePath); + const pdfUrl = await this.uploadBlob(this.pvtBlobService, this.config.privateContainer.azureAccountName, request.getStorageParams().getContainerName(), destPath, pdfFilePath); + this.sendSuccess(res, { id: constants.apiIds.PRINT_API_ID }, { pdfUrl: pdfUrl, ttl: 600 }); + this.sweepFiles([mappedHtmlFilePath, pdfFilePath]) + } catch (err) { + logger.error("PrintService:error after dataPromise got:", err); + this.sendServerError(res, { id: constants.apiIds.PRINT_API_ID }); + } + }).catch(function (err) { + logger.error("PrintService:error in dataPromise got:", err); + this.sendServerError(res, { id: constants.apiIds.PRINT_API_ID }); + }) + } catch (error) { + logger.error("PrintService:Errors:", error); + this.sendServerError(res, { id: constants.apiIds.PRINT_API_ID }); + } + })(); + } + getComposedRequest(reqMap) { + var requestId = uuidv1(); + var contextMap = reqMap.context || {}; + var htmlTemplate = reqMap.htmlTemplate; + var storageParams = this.getStorageDetails(reqMap); + var request = new Request(contextMap, htmlTemplate, requestId, storageParams); + return request; + } + getStorageDetails(reqMap) { + var storage = new StorageParams(); + if (reqMap.storageParams != null) { + storage.setPath(reqMap.storageParams.path || serviceName); + storage.setContainerName(reqMap.storageParams.containerName || this.config.privateContainer.azureContainerName); + logger.info("Print-service:getStorageDetails:storage params found in req got:", storage) + return storage; + } + storage.setContainerName(this.config.privateContainer.azureContainerName) + storage.setPath(serviceName) + logger.info("Print-service:getStorageDetails:storage params not found in req:", storage) + return storage; + } + validateRequest(res, request) { + if (!request) { + logger.error("invalid provided request", request) + this.sendClientError(res, { id: constants.apiIds.PRINT_API_ID, params: this.getErrorParamsMap("request") }); + } + if (!request.htmlTemplate) { + logger.error("invalid provided request htmltemplate is missing", request) + this.sendClientError(res, { id: constants.apiIds.PRINT_API_ID, params: this.getErrorParamsMap("request.htmlTemplate") }); + } + } + getErrorParamsMap(missingField) { + var map = { + "errmsg": util.format("Mandatory params %s is required.", missingField) + }; + return map; + } + sweepFiles(filePathsArray) { + filemanager.deleteFiles(filePathsArray) + } generate(req, res) { (async () => { + let browser; try { const url = req.query.fileUrl; if (!url) - this.sendClientError(res, { id: constants.apiIds.PRINT_API_ID }); - const page = await this.browser.newPage(); - await page.goto(url); - const pdfFilePath = this.pdfBasePath + uuidv1() +'.pdf' - await page.pdf({path: pdfFilePath, format: 'A4'}); - page.close(); - const destPath = 'print-service/' + path.basename(pdfFilePath); - const pdfUrl = await this.uploadBlob(this.config.azureAccountName, this.config.azureContainerName, destPath, pdfFilePath); - this.sendSuccess(res, { id: constants.apiIds.PRINT_API_ID }, { pdfUrl: pdfUrl, ttl: 600 }); - } catch (error) { - console.error('Error: '+ JSON.stringify(error)); - this.sendServerError(res, { id: constants.apiIds.PRINT_API_ID }); - } - })(); + this.sendClientError(res, { id: constants.apiIds.PRINT_API_ID }); + var args = constants.argsConfig.DEBUG_MODE + if (!this.detectDebug()) { + args = constants.argsConfig.PROD_MODE + } + browser = await puppeteer.launch(args); + const page = await browser.newPage(); + page.setDefaultNavigationTimeout(0); + await page.goto(url); + const pdfFilePath = this.pdfBasePath + uuidv1() + '.pdf' + await page.pdf({ path: pdfFilePath, format: 'A4', printBackground: true }); + browser.close() + const destPath = serviceName + path.basename(pdfFilePath); + const pdfUrl = await this.uploadBlob(this.blobService, this.config.azureAccountName, this.config.azureContainerName, destPath, pdfFilePath); + this.sendSuccess(res, { id: constants.apiIds.PRINT_API_ID }, { pdfUrl: pdfUrl, ttl: 600 }); + } catch (error) { + console.error('Error: ', error); + this.sendServerError(res, { id: constants.apiIds.PRINT_API_ID }); + if (browser) { + browser.close() + } + } + })(); } - - uploadBlob(accountName, container, destPath, pdfFilePath) { + uploadBlob(blobService, accountName, container, destPath, pdfFilePath) { return new Promise((resolve, reject) => { - this.blobService.createBlockBlobFromLocalFile(container, destPath, pdfFilePath, function(error, result, response){ - if(!error) { + blobService.createBlockBlobFromLocalFile(container, destPath, pdfFilePath, function (error, result, response) { + if (!error) { const pdfUrl = 'https://' + accountName + '.blob.core.windows.net/' + container + '/' + destPath; resolve(pdfUrl); } else { - console.error('Error while uploading blob: '+ JSON.stringify(error)); + console.error('Error while uploading blob: ' + JSON.stringify(error)); reject(error); } }); }) } - health(req, res) { this.sendSuccess(res, { id: constants.apiIds.HEALTH_API_ID }); } - sendServerError(res, options) { const resObj = { id: options.id, @@ -76,9 +184,8 @@ class PrintService { res.status(constants.responseCodes.SERVER_ERROR.code); res.json(resObj); } - sendClientError(res, options) { - const resObj = { + var resObj = { id: options.id, ver: options.ver || constants.apiIds.version, ts: new Date().getTime(), @@ -88,7 +195,6 @@ class PrintService { res.status(constants.responseCodes.CLIENT_ERROR.code); res.json(resObj); } - sendSuccess(res, options, result = {}) { const resObj = { id: options.id, @@ -101,6 +207,9 @@ class PrintService { res.status(constants.responseCodes.SUCCESS.code); res.json(resObj); } + detectDebug() { + logger.info("app running mode", process.env.NODE_ENV); + return (process.env.NODE_ENV !== 'production'); + } } - -module.exports = new PrintService(config); \ No newline at end of file +module.exports = new PrintService(config);