This commit is contained in:
parent
9156b45c30
commit
f54c8ba9c7
|
@ -1,2 +1,3 @@
|
||||||
node_modules
|
node_modules
|
||||||
config.local.yml
|
config.local.yml
|
||||||
|
tmp
|
||||||
|
|
17
Dockerfile
17
Dockerfile
|
@ -14,6 +14,16 @@ RUN curl -fsSL https://deb.nodesource.com/setup_14.x | sudo -E bash - \
|
||||||
&& apt install -y nodejs \
|
&& apt install -y nodejs \
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# Cups
|
||||||
|
|
||||||
|
RUN useradd admin && usermod -g lpadmin admin
|
||||||
|
|
||||||
|
COPY ctzcls-cups_1.1.0-0_amd64.deb /tmp/
|
||||||
|
RUN chmod a+x /tmp/ctzcls-cups_1.1.0-0_amd64.deb \
|
||||||
|
&& dpkg -i /tmp/ctzcls-cups_1.1.0-0_amd64.deb
|
||||||
|
|
||||||
|
# Printnatura
|
||||||
|
|
||||||
WORKDIR /printnatura
|
WORKDIR /printnatura
|
||||||
|
|
||||||
COPY \
|
COPY \
|
||||||
|
@ -22,9 +32,6 @@ COPY \
|
||||||
./
|
./
|
||||||
RUN npm install
|
RUN npm install
|
||||||
|
|
||||||
COPY cupsd.conf /etc/cups/
|
|
||||||
RUN useradd admin && usermod -g lpadmin admin
|
|
||||||
|
|
||||||
COPY \
|
COPY \
|
||||||
main.js \
|
main.js \
|
||||||
print-server.js \
|
print-server.js \
|
||||||
|
@ -33,9 +40,5 @@ COPY \
|
||||||
./
|
./
|
||||||
COPY sql sql
|
COPY sql sql
|
||||||
|
|
||||||
COPY ctzcls-cups_1.1.0-0_amd64.deb /tmp/
|
|
||||||
RUN chmod a+x /tmp/ctzcls-cups_1.1.0-0_amd64.deb \
|
|
||||||
&& dpkg -i /tmp/ctzcls-cups_1.1.0-0_amd64.deb
|
|
||||||
|
|
||||||
ENTRYPOINT ["/printnatura/entrypoint.sh"]
|
ENTRYPOINT ["/printnatura/entrypoint.sh"]
|
||||||
CMD ["node", "main.js"]
|
CMD ["node", "main.js"]
|
10
README.md
10
README.md
|
@ -8,12 +8,13 @@ This project is used to print pdf, it acts as a print server.
|
||||||
## Installaton
|
## Installaton
|
||||||
Clone the repository
|
Clone the repository
|
||||||
```
|
```
|
||||||
$ git clone https://gitea.verdnatura.es/verdnatura/printnatura.git
|
> git clone https://gitea.verdnatura.es/verdnatura/printnatura.git
|
||||||
```
|
```
|
||||||
|
|
||||||
Build
|
Build
|
||||||
```
|
```
|
||||||
$ docker build -t printnatura .
|
> $CUPS_PASSWORD = 1234
|
||||||
|
> docker build --build-arg CUPS_PASSWORD -t printnatura .
|
||||||
```
|
```
|
||||||
|
|
||||||
Create file named "config.local.yml" and put your private configuration:
|
Create file named "config.local.yml" and put your private configuration:
|
||||||
|
@ -35,12 +36,13 @@ refreshRate: 1000
|
||||||
|
|
||||||
Exec
|
Exec
|
||||||
```
|
```
|
||||||
$ docker run --name printnatura -it --rm -v $PWD/config.local.yml:/printnatura/config.local.yml:ro -p 80:631 printnatura
|
> docker run --name printnatura -it --rm -v $PWD/config.local.yml:/printnatura/config.local.yml:ro -v $PWD/cupsd.conf:/etc/cups/cupsd.conf:ro -p 80:631 printnatura
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Bash
|
Bash
|
||||||
```
|
```
|
||||||
$ docker exec -i -t printnatura bash
|
> docker exec -i -t printnatura bash
|
||||||
```
|
```
|
||||||
|
|
||||||
Has a graphical web interface (cupsd.conf)
|
Has a graphical web interface (cupsd.conf)
|
||||||
|
|
|
@ -7,6 +7,7 @@ const axios = require('axios');
|
||||||
const yml = require('require-yml');
|
const yml = require('require-yml');
|
||||||
|
|
||||||
const selectQuery = fs.readFileSync(`sql/selectQueued.sql`).toString();
|
const selectQuery = fs.readFileSync(`sql/selectQueued.sql`).toString();
|
||||||
|
const jobDataQuery = fs.readFileSync(`sql/jobData.sql`).toString();
|
||||||
const jobArgsQuery = fs.readFileSync(`sql/jobArgs.sql`).toString();
|
const jobArgsQuery = fs.readFileSync(`sql/jobArgs.sql`).toString();
|
||||||
const updateQuery = fs.readFileSync(`sql/updateState.sql`).toString();
|
const updateQuery = fs.readFileSync(`sql/updateState.sql`).toString();
|
||||||
const appDir = path.dirname(require.main.filename);
|
const appDir = path.dirname(require.main.filename);
|
||||||
|
@ -92,7 +93,6 @@ class PrintServer {
|
||||||
}
|
}
|
||||||
|
|
||||||
jobId = printJob.id;
|
jobId = printJob.id;
|
||||||
this.method = printJob.method;
|
|
||||||
|
|
||||||
await conn.query(updateQuery, ['printing', null, jobId]);
|
await conn.query(updateQuery, ['printing', null, jobId]);
|
||||||
await conn.commit();
|
await conn.commit();
|
||||||
|
@ -102,22 +102,27 @@ class PrintServer {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
// Job data
|
||||||
|
const [[jobData]] = await conn.query(jobDataQuery, jobId);
|
||||||
const args = {};
|
const args = {};
|
||||||
const res = await conn.query(jobArgsQuery, jobId);
|
const [res] = await conn.query(jobArgsQuery, jobId);
|
||||||
for (const row of res[0])
|
for (const row of res)
|
||||||
args[row.name] = row.value;
|
args[row.name] = row.value;
|
||||||
try {
|
|
||||||
// Path params
|
// Path params
|
||||||
const usedParams = new Set();
|
const usedParams = new Set();
|
||||||
let methodPath = this.method.replace(/{\w+}/g, function(match) {
|
const methodPath = jobData.method.replace(/{\w+}/g, function(match) {
|
||||||
const key = match.substr(1, match.length - 2);
|
const key = match.substr(1, match.length - 2);
|
||||||
const value = args[key];
|
const value = args[key];
|
||||||
usedParams.add(key);
|
usedParams.add(key);
|
||||||
return value !== undefined ? value : match;
|
return value !== undefined ? value : match;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let pdfData;
|
||||||
|
|
||||||
|
for (let attempts = 0; !pdfData && attempts <= 1; attempts++) {
|
||||||
// URL params
|
// URL params
|
||||||
let params = {
|
const params = {
|
||||||
access_token: this.token,
|
access_token: this.token,
|
||||||
userFk: printJob.userFk
|
userFk: printJob.userFk
|
||||||
};
|
};
|
||||||
|
@ -128,34 +133,34 @@ class PrintServer {
|
||||||
const urlParams = new URLSearchParams(params);
|
const urlParams = new URLSearchParams(params);
|
||||||
|
|
||||||
// Request
|
// Request
|
||||||
const response = await axios({
|
try {
|
||||||
method: 'get',
|
const response = await axios({
|
||||||
url: `${conf.salix.url}/api/${methodPath}?${urlParams.toString()}`,
|
method: 'get',
|
||||||
responseType: 'arraybuffer',
|
url: `${conf.salix.url}/api/${methodPath}?${urlParams.toString()}`,
|
||||||
headers: {
|
responseType: 'arraybuffer',
|
||||||
'Accept': 'application/pdf'
|
headers: {
|
||||||
}
|
'Accept': 'application/pdf'
|
||||||
});
|
|
||||||
this.pdfData = response.data
|
|
||||||
}
|
|
||||||
catch (err) {
|
|
||||||
switch(err.response.statusText) {
|
|
||||||
case 'Unauthorized':
|
|
||||||
try {
|
|
||||||
await this.getToken();
|
|
||||||
} catch(err) {
|
|
||||||
throw new Error(`Could not get token: ${err.message}`);
|
|
||||||
}
|
}
|
||||||
break;
|
});
|
||||||
|
pdfData = response.data;
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
if (err.response?.statusText === 'Unauthorized') {
|
||||||
|
await this.getToken();
|
||||||
|
} else
|
||||||
|
throw err;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const printer = printJob.printer;
|
|
||||||
|
// Save PDF to disk
|
||||||
|
const printer = jobData.printer;
|
||||||
const tmpPath = path.join(appDir, 'tmp')
|
const tmpPath = path.join(appDir, 'tmp')
|
||||||
if (!fs.existsSync(tmpPath))
|
if (!fs.existsSync(tmpPath))
|
||||||
fs.mkdirSync(tmpPath)
|
fs.mkdirSync(tmpPath)
|
||||||
const tmpFilePath = path.join(tmpPath, `${Math.random().toString(36).substring(7)}.pdf`);
|
const tmpFilePath = path.join(tmpPath, `${Math.random().toString(36).substring(7)}.pdf`);
|
||||||
await fs.writeFile(tmpFilePath, this.pdfData, 'binary');
|
await fs.writeFile(tmpFilePath, pdfData, 'binary');
|
||||||
|
|
||||||
|
// Print PDF
|
||||||
try {
|
try {
|
||||||
await pExec(`lp -d "${printer}" "${tmpFilePath}"`);
|
await pExec(`lp -d "${printer}" "${tmpFilePath}"`);
|
||||||
} catch(err) {
|
} catch(err) {
|
||||||
|
@ -166,7 +171,7 @@ class PrintServer {
|
||||||
await conn.query(updateQuery, ['printed', null, jobId]);
|
await conn.query(updateQuery, ['printed', null, jobId]);
|
||||||
|
|
||||||
if (conf.debug)
|
if (conf.debug)
|
||||||
console.debug(`(${colors.yellow(jobId)}) Document has been printed`, `[${args.collectionFk}, ${printJob.report}, ${printer}]`.green);
|
console.debug(`(${colors.yellow(jobId)}) Document has been printed`, `[${args.collectionFk}, ${jobData.report}, ${printer}]`.green);
|
||||||
|
|
||||||
await fs.unlink(tmpFilePath);
|
await fs.unlink(tmpFilePath);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
SELECT r.name report,
|
||||||
|
p.name printer,
|
||||||
|
r.method
|
||||||
|
FROM printQueue pq
|
||||||
|
JOIN report r ON r.id = pq.reportFk
|
||||||
|
JOIN printer p ON p.id = pq.printerFk
|
||||||
|
WHERE pq.id = ?
|
|
@ -1,15 +1,9 @@
|
||||||
SELECT pq.id,
|
SELECT pq.id,
|
||||||
r.name report,
|
|
||||||
p.name printer,
|
|
||||||
pqa.name arg,
|
|
||||||
pqa.value,
|
|
||||||
r.method,
|
|
||||||
pq.workerFk userFk
|
pq.workerFk userFk
|
||||||
FROM printQueue pq
|
FROM printQueue pq
|
||||||
JOIN report r ON r.id = pq.reportFk
|
JOIN report r ON r.id = pq.reportFk
|
||||||
JOIN printQueueArgs pqa ON pqa.printQueueFk = pq.id
|
|
||||||
LEFT JOIN printer p ON p.id = pq.printerFk
|
|
||||||
WHERE pq.statusCode = 'queued'
|
WHERE pq.statusCode = 'queued'
|
||||||
AND NOT r.method IS NULL
|
AND r.method IS NOT NULL
|
||||||
ORDER BY pq.priorityFk ASC
|
ORDER BY pq.priorityFk ASC
|
||||||
LIMIT 1
|
LIMIT 1
|
||||||
|
FOR UPDATE
|
||||||
|
|
Loading…
Reference in New Issue