const mysql = require('mysql2/promise'); const exec = require('child_process').exec; const fs = require('fs-extra'); const path = require('path'); const colors = require('colors'); const axios = require('axios'); const yml = require('require-yml'); const selectQuery = fs.readFileSync(`sql/selectQueued.sql`).toString(); const jobArgsQuery = fs.readFileSync(`sql/jobArgs.sql`).toString(); const updateQuery = fs.readFileSync(`sql/updateState.sql`).toString(); const appDir = path.dirname(require.main.filename); class PrintServer { async start() { let conf = yml(path.join(__dirname, 'config.yml')); const localConfFile = path.join(__dirname, 'config.local.yml'); if (fs.existsSync(localConfFile)) conf = Object.assign({}, conf, yml(localConfFile)); this.conf = conf; console.clear(); console.log(` ░░░▒▒▒▓▓▓███││ │ ${colors.bgCyan.black(' PDF Print Server ')} │ ││███▓▓▓▒▒▒░░░`) console.log(''); await this.init(); } async init() { this.conn = await mysql.createConnection(this.conf.db); this.conn.on('error', err => this.onDbError(err)); await this.getToken(); await this.poll(); } async stop() { await this.end(); } async end() { clearTimeout(this.pollTimeout); await this.conn.end(); if (this.reconnectTimeout) { clearTimeout(this.reconnectTimeout); this.reconnectTimeout = null; } } async getToken() { let response = await axios.post(`${this.conf.salix.url}/api/Accounts/login`, { user: this.conf.salix.user, password: this.conf.salix.password }); this.token = response.data.token } async onDbError(err) { switch(err.code) { case 'CONNECTION_CLOSE': try { await this.end(); } catch(e) {} await this.reconnect(); break; } } async reconnect() { this.reconnectTimeout = null; try { await this.init(); } catch (err) { this.reconnectTimeout = setTimeout(reconnect, this.conf.reconnectTimeout * 1000); } } async poll() { try { await this.printJob(); } catch (err) { console.error(err) this.reconnect(); } this.pollTimeout = setTimeout(() => this.poll(), this.conf.refreshRate); } async printJob() { const conn = this.conn; const conf = this.conf; let printJob; let jobId; try { await conn.beginTransaction(); [[printJob]] = await conn.query(selectQuery, 'LabelCollection'); if (!printJob) return; jobId = printJob.id; await conn.query(updateQuery, ['printing', null, jobId]); await conn.commit(); } catch (err) { conn.rollback(); throw err; } try { const args = {}; const res = await conn.query(jobArgsQuery, jobId); for (const row of res[0]) args[row.name] = row.value; const response = await axios({ method: 'get', url: `${conf.salix.url}/api/Tickets/${args.param}/collection-label?access_token=${this.token}`, responseType: 'arraybuffer', headers: { 'Content-Type': 'application/json', 'Accept': 'application/pdf' } }); const printer = printJob.printer; const tmpFilePath = path.join(appDir, 'tmp', `${Math.random().toString(36).substring(7)}.pdf`); await fs.writeFile(tmpFilePath, response.data, 'binary'); try { await pExec(`lp -d "${printer}" "${tmpFilePath}"`); } catch(err) { await fs.unlink(tmpFilePath); throw new Error(`The printer could not be accessed: ${printer}: ${err.message}`); } await conn.query(updateQuery, ['printed', null, jobId]); if (conf.debug) console.debug(`Document has been printed`, jobId, printer); await fs.unlink(tmpFilePath); } catch (err) { switch(err.code) { case 'ERR_BAD_REQUEST': try { await this.getToken(); } catch(err) { console.error(err); } break; } await this.conn.query(updateQuery, ['error', err.message, jobId]); throw new Error(`(${jobId}) ${err.message}`); } } logError() { console.log('[ERROR]'.red.bold, `(${id}) ${message}:`, param.red); } logMessage() { console.log(' [OK]'.green.bold, `(${id}) ${message}:`, param.green); } } module.exports = PrintServer; function pExec(command) { return new Promise(function(resolve, reject) { exec(command, function(err, stdout, stderr) { if (err) return reject(err); resolve({stdout, stderr}) }); }); }