Compare commits
2 Commits
685c7ae6a0
...
8b1857bc3b
Author | SHA1 | Date |
---|---|---|
radex | 8b1857bc3b | |
radex | 64ff461da5 |
|
@ -27,13 +27,13 @@ PRINTSERVANT_SECRET - Optional, used to substitute $SECRET in printer configs' i
|
|||
```
|
||||
|
||||
|
||||
```
|
||||
PRINTSERVANT_CONFIG='{"printers":[{"name":"DYMO_LabelWriter450","aliases":["dymo"],"ipp_url":"ipp://printmaster.local:631/printers/DYMO_LabelWriter450"}]}' node index.js
|
||||
```sh
|
||||
PRINTSERVANT_CONFIG=$(cat dev-config.json) node index.js
|
||||
```
|
||||
|
||||
or:
|
||||
|
||||
```
|
||||
```sh
|
||||
docker build -t printservant .
|
||||
docker run --env PRINTSERVANT_CONFIG='... put config here ...' -it -p 3199:3199 printservant
|
||||
docker run --env PRINTSERVANT_CONFIG=$(cat dev-config.json) -it -p 3199:3199 printservant
|
||||
```
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
{
|
||||
"printers": [
|
||||
{
|
||||
"name": "DYMO_LabelWriter450",
|
||||
"aliases": ["dymo", "label"],
|
||||
"ipp_url": "ipp://printmaster.local:443/printers/DYMO_LabelWriter450"
|
||||
},
|
||||
{
|
||||
"name": "Zebra_GK420T",
|
||||
"aliases": ["zebra", "biglabel", "4x6"],
|
||||
"ipp_url": "ipp://printmaster.local:443/printers/Zebra_GK420T"
|
||||
},
|
||||
{
|
||||
"name": "Brother_MFC8380DN",
|
||||
"aliases": ["brother", "franciszek", "a4"],
|
||||
"ipp_url": "ipp://printmaster.local:443/printers/Brother_MFC8380DN"
|
||||
},
|
||||
{
|
||||
"name": "OKI_C332",
|
||||
"aliases": ["oki", "okił", "a4_color"],
|
||||
"ipp_url": "ipp://printmaster.local:443/printers/OKI_C332"
|
||||
}
|
||||
]
|
||||
}
|
29
index.js
29
index.js
|
@ -9,6 +9,7 @@ const bodyParser = require('body-parser')
|
|||
const PORT = Number(process.env.PRINTSERVANT_PORT) || 3199
|
||||
const CONFIG = JSON.parse(process.env.PRINTSERVANT_CONFIG || 'null')
|
||||
const SECRET = process.env.PRINTSERVANT_SECRET || ''
|
||||
const MAX_SIZE_MB = Number(process.env.PRINTSERVANT_MAX_SIZE_MB) || 10
|
||||
|
||||
if (!(PORT && CONFIG)) {
|
||||
throw new Error("PRINTSERVANT_PORT and PRINTSERVANT_CONFIG environment variables must be set")
|
||||
|
@ -59,10 +60,11 @@ app.get('/', (req, res) => {
|
|||
- Prints IPP attributes
|
||||
- GET /printers/:name/jobs
|
||||
- Prints IPP jobs
|
||||
- POST /print?printer=:name (body: application/pdf or image/png)
|
||||
- POST /print?printer=:name
|
||||
- Prints a PDF
|
||||
- ?printer= printer name or alias (case insensitive)
|
||||
- ?copies= (optional) number of copies (1-10 allowed)
|
||||
- Body: PDF or PNG data; *or* any data if Content-Type header is passed
|
||||
- Response: 200 OK if sent to printer successfully (not necessarily printed), 4xx/5xx otherwise
|
||||
</pre>`)
|
||||
})
|
||||
|
@ -107,9 +109,9 @@ app.get('/printers/:name/jobs', (req, res) => {
|
|||
|
||||
// --- Print API ---
|
||||
|
||||
app.post('/print', bodyParser.raw({ type: '*/*', limit: 10_000_000 }), (req, res) => {
|
||||
const { body, query } = req
|
||||
console.log("Received print job: ", { body, query })
|
||||
app.post('/print', bodyParser.raw({ type: '*/*', limit: MAX_SIZE_MB * 1_000_000 }), (req, res) => {
|
||||
const { body, query, headers } = req
|
||||
console.log("Received print job: ", { body, query, headers })
|
||||
|
||||
// validate query params
|
||||
const { printer: printerName, copies: copiesParam, ...otherParams } = query
|
||||
|
@ -130,15 +132,21 @@ app.post('/print', bodyParser.raw({ type: '*/*', limit: 10_000_000 }), (req, res
|
|||
|
||||
// validate body
|
||||
if (!body || !body.length) {
|
||||
res.status(400).send("No data received, please send a PDF")
|
||||
res.status(400).send("No data received")
|
||||
return
|
||||
}
|
||||
|
||||
const contentType = headers['content-type']
|
||||
const hasValidContentType = contentType
|
||||
&& contentType !== 'application/octet-stream'
|
||||
&& contentType !== 'application/x-www-form-urlencoded'
|
||||
|
||||
const isPDF = body.subarray(0, 4).toString() === "%PDF"
|
||||
const isPNG = body.subarray(0, 8).equals(Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]))
|
||||
|
||||
if (!isPDF && !isPNG) {
|
||||
res.status(415).send("Unsupported media type, only PDF and PNG are supported")
|
||||
const hasValidFormat = isPDF || isPNG || hasValidContentType
|
||||
if (!hasValidFormat) {
|
||||
res.status(415).send("Unknown media type. Pass a PDF or PNG file, or set Content-Type header to print something else.")
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -152,7 +160,12 @@ app.post('/print', bodyParser.raw({ type: '*/*', limit: 10_000_000 }), (req, res
|
|||
// send print job
|
||||
const msg = {
|
||||
"operation-attributes-tag": {
|
||||
"document-format": isPNG ? "image/png" : "application/pdf",
|
||||
"document-format": (() => {
|
||||
if (isPDF) return "application/pdf"
|
||||
if (isPNG) return "image/png"
|
||||
if (contentType) return contentType
|
||||
throw new Error('unreachable')
|
||||
})(),
|
||||
},
|
||||
"job-attributes-tag": {
|
||||
"copies": copies,
|
||||
|
|
Loading…
Reference in New Issue