diff --git a/.dockerignore b/.dockerignore index 752df873..7d350ac2 100644 --- a/.dockerignore +++ b/.dockerignore @@ -15,6 +15,7 @@ bfx-reports-framework/config/*.json bfx-reports-framework/config/facs/*.json bfx-reports-framework/logs/*.log bfx-reports-framework/csv +bfx-reports-framework/report-files bfx-report-ui/build bfx-report-ui/bfx-report-express/logs/*.log bfx-report-ui/bfx-report-express/config/*.json diff --git a/electron-builder-config.js b/electron-builder-config.js index 0938f268..69561b2c 100644 --- a/electron-builder-config.js +++ b/electron-builder-config.js @@ -155,6 +155,7 @@ module.exports = { '!bfx-reports-framework/*/*.sh', '!bfx-reports-framework/*.sh', '!bfx-reports-framework/.mocharc.json', + '!bfx-reports-framework/node_modules/phantomjs-prebuilt', '!**/.dockerignore', '!**/*Dockerfile*', diff --git a/server.js b/server.js index 2b1641f3..056e6fad 100644 --- a/server.js +++ b/server.js @@ -32,7 +32,7 @@ const { const { WrongPathToUserDataError, - WrongPathToUserCsvError, + WrongPathToUserReportFilesError, WrongSecretKeyError } = require('./src/errors') @@ -76,7 +76,9 @@ const allowedProcessMessagesSet = _getAllowedStatesSet({ 'RESPONSE_GET_BACKUP_FILES_METADATA', - 'RESPONSE_UPDATE_USERS_SYNC_ON_STARTUP_REQUIRED_STATE' + 'RESPONSE_UPDATE_USERS_SYNC_ON_STARTUP_REQUIRED_STATE', + + 'REQUEST_PDF_CREATION' ] }) const allowedProcessStatesSet = _getAllowedStatesSet({ @@ -92,14 +94,16 @@ const allowedProcessStatesSet = _getAllowedStatesSet({ 'REQUEST_GET_BACKUP_FILES_METADATA', - 'REQUEST_UPDATE_USERS_SYNC_ON_STARTUP_REQUIRED_STATE' + 'REQUEST_UPDATE_USERS_SYNC_ON_STARTUP_REQUIRED_STATE', + + 'RESPONSE_PDF_CREATION' ] }) ;(async () => { try { const pathToUserData = process.env.PATH_TO_USER_DATA - const pathToUserCsv = process.env.PATH_TO_USER_CSV + const pathToUserReportFiles = process.env.PATH_TO_USER_REPORT_FILES const schedulerRule = process.env.SCHEDULER_RULE const secretKey = process.env.SECRET_KEY const grape1DhtPort = process.env.GRAPE_1DHT_PORT @@ -116,8 +120,8 @@ const allowedProcessStatesSet = _getAllowedStatesSet({ if (!pathToUserData) { throw new WrongPathToUserDataError() } - if (!pathToUserCsv) { - throw new WrongPathToUserCsvError() + if (!pathToUserReportFiles) { + throw new WrongPathToUserReportFilesError() } const grape = `http://127.0.0.1:${grape2ApiPort}` @@ -162,7 +166,7 @@ const allowedProcessStatesSet = _getAllowedStatesSet({ '--isSchedulerEnabled=true', '--isElectronjsEnv=true', '--isLoggerDisabled=false', - `--csvFolder=${pathToUserCsv}`, + `--reportFolder=${pathToUserReportFiles}`, `--tempFolder=${pathToUserData}/temp`, `--logsFolder=${pathToUserData}/logs`, `--dbFolder=${pathToUserData}`, diff --git a/src/change-reports-folder.js b/src/change-reports-folder.js index 59f2f0d1..4bdb9efc 100644 --- a/src/change-reports-folder.js +++ b/src/change-reports-folder.js @@ -2,7 +2,7 @@ const { dialog, BrowserWindow } = require('electron') -const { CSV_PATH_VERSION } = require('./const') +const { REPORT_FILES_PATH_VERSION } = require('./const') const { InvalidFilePathError, @@ -52,8 +52,8 @@ module.exports = ({ pathToUserDocuments }) => { await pauseApp() const isSaved = await getConfigsKeeperByName('main') .saveConfigs({ - csvPathVersion: CSV_PATH_VERSION, - pathToUserCsv: filePaths[0] + reportFilesPathVersion: REPORT_FILES_PATH_VERSION, + pathToUserReportFiles: filePaths[0] }) if (!isSaved) { diff --git a/src/const.js b/src/const.js index 400c7df6..df3cc2f1 100644 --- a/src/const.js +++ b/src/const.js @@ -6,7 +6,7 @@ const DB_FILE_NAME = 'db-sqlite_sync_m0.db' const DB_SHM_FILE_NAME = `${DB_FILE_NAME}-shm` const DB_WAL_FILE_NAME = `${DB_FILE_NAME}-wal` const SECRET_KEY_FILE_NAME = 'secret-key' -const CSV_PATH_VERSION = 1 +const REPORT_FILES_PATH_VERSION = 1 module.exports = { CONFIGS_FILE_NAME, @@ -15,5 +15,5 @@ module.exports = { DB_SHM_FILE_NAME, DB_WAL_FILE_NAME, SECRET_KEY_FILE_NAME, - CSV_PATH_VERSION + REPORT_FILES_PATH_VERSION } diff --git a/src/errors/index.js b/src/errors/index.js index 98a6119e..0b26dbfd 100644 --- a/src/errors/index.js +++ b/src/errors/index.js @@ -65,8 +65,8 @@ class WrongPathToUserDataError extends BaseError { } } -class WrongPathToUserCsvError extends BaseError { - constructor (message = 'ERR_WRONG_PATH_TO_USER_CSV') { +class WrongPathToUserReportFilesError extends BaseError { + constructor (message = 'ERR_WRONG_PATH_TO_USER_REPORT_FILES') { super(message) } } @@ -124,7 +124,7 @@ module.exports = { AppInitializationError, FreePortError, WrongPathToUserDataError, - WrongPathToUserCsvError, + WrongPathToUserReportFilesError, WrongSecretKeyError, ReportsFolderChangingError, SyncFrequencyChangingError, diff --git a/src/initialize-app.js b/src/initialize-app.js index 6f5d6298..763f6361 100644 --- a/src/initialize-app.js +++ b/src/initialize-app.js @@ -3,7 +3,7 @@ const { app } = require('electron') const path = require('path') -const { CSV_PATH_VERSION } = require('./const') +const { REPORT_FILES_PATH_VERSION } = require('./const') const triggerSyncAfterUpdates = require('./trigger-sync-after-updates') const triggerElectronLoad = require('./trigger-electron-load') @@ -41,6 +41,7 @@ const enforceMacOSAppLocation = require( const manageWorkerMessages = require( './manage-worker-messages' ) +const printToPDF = require('./print-to-pdf') const pathToLayouts = path.join(__dirname, 'layouts') const pathToLayoutAppInitErr = path @@ -50,52 +51,26 @@ const { rule: schedulerRule } = require( '../bfx-reports-framework/config/schedule.json' ) -const _resetCsvPath = async ( +const _resetReportFilesPath = async ( configsKeeper, opts = {} ) => { const { - pathToUserCsv, - isRelativeCsvPath + pathToUserReportFiles } = opts - // Need to use a new csv folder path for export - const storedPathToUserCsv = configsKeeper - .getConfigByName('pathToUserCsv') - const csvPathVersion = configsKeeper - .getConfigByName('csvPathVersion') - - if (csvPathVersion === CSV_PATH_VERSION) { - return - } - if ( - ( - isRelativeCsvPath && - !storedPathToUserCsv.endsWith('csv') - ) || - ( - !isRelativeCsvPath && - !storedPathToUserCsv.endsWith('bitfinex/reports') - ) || - ( - !isRelativeCsvPath && - !path.isAbsolute(storedPathToUserCsv) - ) || - ( - isRelativeCsvPath && - path.isAbsolute(storedPathToUserCsv) - ) - ) { - await configsKeeper.saveConfigs({ - csvPathVersion: CSV_PATH_VERSION, - pathToUserCsv - }) + // Need to use a new report folder path for export + const reportFilesPathVersion = configsKeeper + .getConfigByName('reportFilesPathVersion') ?? + configsKeeper.getConfigByName('csvPathVersion') // For back compatibility + if (reportFilesPathVersion === REPORT_FILES_PATH_VERSION) { return } await configsKeeper.saveConfigs({ - csvPathVersion: CSV_PATH_VERSION + reportFilesPathVersion: REPORT_FILES_PATH_VERSION, + pathToUserReportFiles }) } @@ -153,7 +128,7 @@ const _manageConfigs = (params = {}) => { pathToUserDocuments } = params - const pathToUserCsv = path.join( + const pathToUserReportFiles = path.join( pathToUserDocuments, 'bitfinex/reports' ) @@ -161,18 +136,15 @@ const _manageConfigs = (params = {}) => { const configsKeeper = configsKeeperFactory( { pathToUserData }, { - pathToUserCsv, + pathToUserReportFiles, schedulerRule, shownChangelogVer: '0.0.0', triggeredSyncAfterUpdatesVer: '0.0.0' } ) - _resetCsvPath( + _resetReportFilesPath( configsKeeper, - { - pathToUserCsv, - isRelativeCsvPath: true - } + { pathToUserReportFiles } ) } @@ -222,6 +194,8 @@ module.exports = async () => { await triggerElectronLoad(portsMap) await checkForUpdatesAndNotify() await manageChangelog() + + printToPDF() } catch (err) { await createErrorWindow(pathToLayoutAppInitErr) diff --git a/src/print-to-pdf/index.js b/src/print-to-pdf/index.js new file mode 100644 index 00000000..150b421a --- /dev/null +++ b/src/print-to-pdf/index.js @@ -0,0 +1,109 @@ +'use strict' + +const { BrowserWindow } = require('electron') +const fs = require('fs/promises') +const path = require('path') + +const ipcs = require('../ipcs') +const wins = require('../windows') + +const PROCESS_MESSAGES = require( + '../../bfx-reports-framework/workers/loc.api/process.message.manager/process.messages' +) +const PROCESS_STATES = require( + '../../bfx-reports-framework/workers/loc.api/process.message.manager/process.states' +) + +module.exports = () => { + ipcs.serverIpc.on('message', async (mess) => { + try { + if (mess?.state !== PROCESS_MESSAGES.REQUEST_PDF_CREATION) { + return + } + + const { + templateFilePath, + template = 'No data', + format = 'portrait', + orientation = 'Letter', + uid = null + } = mess?.data ?? {} + + const isTemplateFilePathUsed = ( + templateFilePath && + typeof templateFilePath === 'string' + ) + + const html = isTemplateFilePathUsed + ? await fs.readFile(templateFilePath, { encoding: 'utf8' }) + : template + + if (isTemplateFilePathUsed) { + await fs.rm(templateFilePath, { force: true, maxRetries: 3 }) + } + + const win = new BrowserWindow({ + show: false, + parent: wins.mainWindow, + webPreferences: { + nodeIntegration: true + } + }) + win.loadURL(`data:text/html;charset=utf-8,${encodeURIComponent(html)}`) + + await new Promise((resolve, reject) => { + win.webContents.on('did-finish-load', resolve) + win.webContents.on('did-fail-load', (e, code, err) => { + reject(err) + }) + }) + const buffer = await win.webContents.printToPDF({ + landscape: format !== 'portrait', + pageSize: orientation, + margins: { + top: 0, + bottom: 0, + left: 0, + right: 0 + }, + displayHeaderFooter: true, + footerTemplate: `\ + + Page from +` + }) + + if (isTemplateFilePathUsed) { + const { dir, name } = path.parse(templateFilePath) + const pdfFilePath = path.format({ dir, name, ext: '.pdf' }) + + await fs.writeFile(pdfFilePath, buffer) + + ipcs.serverIpc.send({ + state: PROCESS_STATES.RESPONSE_PDF_CREATION, + data: { pdfFilePath, uid } + }) + + return + } + + ipcs.serverIpc.send({ + state: PROCESS_STATES.RESPONSE_PDF_CREATION, + data: { buffer, uid } + }) + } catch (err) { + ipcs.serverIpc.send({ + state: PROCESS_STATES.RESPONSE_PDF_CREATION, + data: { err, uid: mess?.data?.uid ?? null } + }) + + console.error(err) + } + }) +} diff --git a/src/run-server.js b/src/run-server.js index 741881b4..03b785d1 100644 --- a/src/run-server.js +++ b/src/run-server.js @@ -28,8 +28,8 @@ module.exports = ({ const env = { ...process.env, PATH_TO_USER_DATA: pathToUserData, - PATH_TO_USER_CSV: mainConfsKeeper - .getConfigByName('pathToUserCsv'), + PATH_TO_USER_REPORT_FILES: mainConfsKeeper + .getConfigByName('pathToUserReportFiles'), SCHEDULER_RULE: mainConfsKeeper .getConfigByName('schedulerRule'), SECRET_KEY: secretKey,