From: Dan Brown Date: Fri, 4 Jun 2021 23:28:08 +0000 (+0100) Subject: Added node docx-to-page example X-Git-Url: http://source.bookstackapp.com/api-scripts/commitdiff_plain/04b4fca337add86e38286fd9cfb7f1a0b5be9bb8 Added node docx-to-page example --- diff --git a/node-docx-to-page/index.js b/node-docx-to-page/index.js new file mode 100644 index 0000000..ca2fb85 --- /dev/null +++ b/node-docx-to-page/index.js @@ -0,0 +1,84 @@ +// Libraries used +const fs = require('fs'); +const path = require('path'); +const axios = require('axios'); +const mammoth = require('mammoth'); + +// BookStack API variables +// Uses values on the environment unless hardcoded +// To hardcode, add values to the empty strings in the below. +const bookStackConfig = { + base_url: '' || process.env.BS_URL, + token_id: '' || process.env.BS_TOKEN_ID, + token_secret: '' || process.env.BS_TOKEN_SECRET, +}; + +// Script Logic +//////////////// + +// Check arguments provided +if (process.argv.length < 4) { + console.error('Both and arguments need to be provided'); + return; +} + +// Get arguments passed via command +const [_exec, _script, docxFile, bookSlug] = process.argv; + +// Check the docx file exists +if (!fs.existsSync(docxFile)) { + console.error(`Provided docx file "${docxFile}" could not be found`); + return; +} + +// Create an axios instance for our API +const api = axios.create({ + baseURL: bookStackConfig.base_url.replace(/\/$/, '') + '/api/', + timeout: 5000, + headers: { 'Authorization' : `Token ${bookStackConfig.token_id}:${bookStackConfig.token_secret}` }, +}); + +// Wrap the rest of our code in an async function so we can await within. +(async function() { + + // Fetch the related book to ensure it exists + const {data: bookSearch} = await api.get(`/books?filter[slug]=${encodeURIComponent(bookSlug)}`); + if (bookSearch.data.length === 0) { + console.error(`Book with a slug of "${bookSlug}" could not be found`); + return; + } + const book = bookSearch.data[0]; + + // Convert our document + const {value: html, messages} = await mammoth.convertToHtml({path: docxFile}); + + // Create a name from our document file name + let {name} = path.parse(docxFile); + name = name.replace(/[-_]/g, ' '); + + // Upload our page + const {data: page} = await api.post('/pages', { + book_id: book.id, + name, + html, + }); + + // Output the results + console.info(`File converted and created as a page.`); + console.info(` - Page ID: ${page.id}`); + console.info(` - Page Name: ${page.name}`); + console.info(`====================================`); + console.info(`Conversion occurred with ${messages.length} message(s):`); + for (const message of messages) { + console.warn(`[${message.type}] ${message.message}`); + } + +})().catch(err => { + // Handle API errors + if (err.response) { + console.error(`Request failed with status ${err.response.status} [${err.response.statusText}]`); + return; + } + // Output all other errors + console.error(err) +}); \ No newline at end of file diff --git a/node-docx-to-page/package-lock.json b/node-docx-to-page/package-lock.json new file mode 100644 index 0000000..8ea6be1 --- /dev/null +++ b/node-docx-to-page/package-lock.json @@ -0,0 +1,258 @@ +{ + "name": "docx-to-page", + "version": "1.0.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "docx-to-page", + "version": "1.0.0", + "license": "MIT", + "dependencies": { + "axios": "^0.21.1", + "mammoth": "^1.4.17" + } + }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/axios": { + "version": "0.21.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.1.tgz", + "integrity": "sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==", + "dependencies": { + "follow-redirects": "^1.10.0" + } + }, + "node_modules/bluebird": { + "version": "3.4.7", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.4.7.tgz", + "integrity": "sha1-9y12C+Cbf3bQjtj66Ysomo0F+rM=" + }, + "node_modules/dingbat-to-unicode": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dingbat-to-unicode/-/dingbat-to-unicode-1.0.1.tgz", + "integrity": "sha512-98l0sW87ZT58pU4i61wa2OHwxbiYSbuxsCBozaVnYX2iCnr3bLM3fIes1/ej7h1YdOKuKt/MLs706TVnALA65w==" + }, + "node_modules/duck": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/duck/-/duck-0.1.12.tgz", + "integrity": "sha512-wkctla1O6VfP89gQ+J/yDesM0S7B7XLXjKGzXxMDVFg7uEn706niAtyYovKbyq1oT9YwDcly721/iUWoc8MVRg==", + "dependencies": { + "underscore": "^1.13.1" + } + }, + "node_modules/follow-redirects": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.1.tgz", + "integrity": "sha512-HWqDgT7ZEkqRzBvc2s64vSZ/hfOceEol3ac/7tKwzuvEyWx3/4UegXh5oBOIotkGsObyk3xznnSRVADBgWSQVg==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/jszip": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-2.5.0.tgz", + "integrity": "sha1-dET9hVHd8+XacZj+oMkbyDCMwnQ=", + "dependencies": { + "pako": "~0.2.5" + } + }, + "node_modules/lop": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/lop/-/lop-0.4.1.tgz", + "integrity": "sha512-9xyho9why2A2tzm5aIcMWKvzqKsnxrf9B5I+8O30olh6lQU8PH978LqZoI4++37RBgS1Em5i54v1TFs/3wnmXQ==", + "dependencies": { + "duck": "^0.1.12", + "option": "~0.2.1", + "underscore": "^1.13.1" + } + }, + "node_modules/mammoth": { + "version": "1.4.17", + "resolved": "https://registry.npmjs.org/mammoth/-/mammoth-1.4.17.tgz", + "integrity": "sha512-/YTeOtKsrPWwFt6dufmqXyJEq4En1GXloU4cYU7x4nnIa51H6AIRelkSba/m+6Emg7rZwIxGxa1hg7S3dK8M9Q==", + "dependencies": { + "argparse": "~1.0.3", + "bluebird": "~3.4.0", + "dingbat-to-unicode": "^1.0.1", + "jszip": "~2.5.0", + "lop": "^0.4.1", + "path-is-absolute": "^1.0.0", + "sax": "~1.1.1", + "underscore": "^1.13.1", + "xmlbuilder": "^10.0.0" + }, + "bin": { + "mammoth": "bin/mammoth" + } + }, + "node_modules/option": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/option/-/option-0.2.4.tgz", + "integrity": "sha1-/Udc35jcq7PLOXo7pShP60Xtv+Q=" + }, + "node_modules/pako": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/pako/-/pako-0.2.9.tgz", + "integrity": "sha1-8/dSL073gjSNqBYbrZ7P1Rv4OnU=" + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sax": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.1.6.tgz", + "integrity": "sha1-XWFr6KXmB9VOEUr65Vt+ry/MMkA=" + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" + }, + "node_modules/underscore": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.1.tgz", + "integrity": "sha512-hzSoAVtJF+3ZtiFX0VgfFPHEDRm7Y/QPjGyNo4TVdnDTdft3tr8hEkD25a1jC+TjTuE7tkHGKkhwCgs9dgBB2g==" + }, + "node_modules/xmlbuilder": { + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-10.1.1.tgz", + "integrity": "sha512-OyzrcFLL/nb6fMGHbiRDuPup9ljBycsdCypwuyg5AAHvyWzGfChJpCXMG88AGTIMFhGZ9RccFN1e6lhg3hkwKg==", + "engines": { + "node": ">=4.0" + } + } + }, + "dependencies": { + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "axios": { + "version": "0.21.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.1.tgz", + "integrity": "sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==", + "requires": { + "follow-redirects": "^1.10.0" + } + }, + "bluebird": { + "version": "3.4.7", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.4.7.tgz", + "integrity": "sha1-9y12C+Cbf3bQjtj66Ysomo0F+rM=" + }, + "dingbat-to-unicode": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dingbat-to-unicode/-/dingbat-to-unicode-1.0.1.tgz", + "integrity": "sha512-98l0sW87ZT58pU4i61wa2OHwxbiYSbuxsCBozaVnYX2iCnr3bLM3fIes1/ej7h1YdOKuKt/MLs706TVnALA65w==" + }, + "duck": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/duck/-/duck-0.1.12.tgz", + "integrity": "sha512-wkctla1O6VfP89gQ+J/yDesM0S7B7XLXjKGzXxMDVFg7uEn706niAtyYovKbyq1oT9YwDcly721/iUWoc8MVRg==", + "requires": { + "underscore": "^1.13.1" + } + }, + "follow-redirects": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.1.tgz", + "integrity": "sha512-HWqDgT7ZEkqRzBvc2s64vSZ/hfOceEol3ac/7tKwzuvEyWx3/4UegXh5oBOIotkGsObyk3xznnSRVADBgWSQVg==" + }, + "jszip": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-2.5.0.tgz", + "integrity": "sha1-dET9hVHd8+XacZj+oMkbyDCMwnQ=", + "requires": { + "pako": "~0.2.5" + } + }, + "lop": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/lop/-/lop-0.4.1.tgz", + "integrity": "sha512-9xyho9why2A2tzm5aIcMWKvzqKsnxrf9B5I+8O30olh6lQU8PH978LqZoI4++37RBgS1Em5i54v1TFs/3wnmXQ==", + "requires": { + "duck": "^0.1.12", + "option": "~0.2.1", + "underscore": "^1.13.1" + } + }, + "mammoth": { + "version": "1.4.17", + "resolved": "https://registry.npmjs.org/mammoth/-/mammoth-1.4.17.tgz", + "integrity": "sha512-/YTeOtKsrPWwFt6dufmqXyJEq4En1GXloU4cYU7x4nnIa51H6AIRelkSba/m+6Emg7rZwIxGxa1hg7S3dK8M9Q==", + "requires": { + "argparse": "~1.0.3", + "bluebird": "~3.4.0", + "dingbat-to-unicode": "^1.0.1", + "jszip": "~2.5.0", + "lop": "^0.4.1", + "path-is-absolute": "^1.0.0", + "sax": "~1.1.1", + "underscore": "^1.13.1", + "xmlbuilder": "^10.0.0" + } + }, + "option": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/option/-/option-0.2.4.tgz", + "integrity": "sha1-/Udc35jcq7PLOXo7pShP60Xtv+Q=" + }, + "pako": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/pako/-/pako-0.2.9.tgz", + "integrity": "sha1-8/dSL073gjSNqBYbrZ7P1Rv4OnU=" + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" + }, + "sax": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.1.6.tgz", + "integrity": "sha1-XWFr6KXmB9VOEUr65Vt+ry/MMkA=" + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" + }, + "underscore": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.1.tgz", + "integrity": "sha512-hzSoAVtJF+3ZtiFX0VgfFPHEDRm7Y/QPjGyNo4TVdnDTdft3tr8hEkD25a1jC+TjTuE7tkHGKkhwCgs9dgBB2g==" + }, + "xmlbuilder": { + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-10.1.1.tgz", + "integrity": "sha512-OyzrcFLL/nb6fMGHbiRDuPup9ljBycsdCypwuyg5AAHvyWzGfChJpCXMG88AGTIMFhGZ9RccFN1e6lhg3hkwKg==" + } + } +} diff --git a/node-docx-to-page/package.json b/node-docx-to-page/package.json new file mode 100644 index 0000000..43e27e0 --- /dev/null +++ b/node-docx-to-page/package.json @@ -0,0 +1,16 @@ +{ + "name": "docx-to-page", + "version": "1.0.0", + "description": "This script will take a docx file, attempt to convert it to a BookStack suitable format, then upload it into a BookStack book via the API.", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "Dan Brown", + "license": "MIT", + "dependencies": { + "axios": "^0.21.1", + "mammoth": "^1.4.17" + } +} diff --git a/node-docx-to-page/readme.md b/node-docx-to-page/readme.md new file mode 100644 index 0000000..adedb82 --- /dev/null +++ b/node-docx-to-page/readme.md @@ -0,0 +1,46 @@ +# Convert docx to Page + +This script will take a docx file, +attempt to convert it to a BookStack suitable format, then upload +it into a BookStack book via the API. + +This is a simplistic example of a NodeJS script. You will likely want to +alter and extend this script to suit your use-case. + +## Requirements + +You will need NodeJS installed (Tested on v14, may work on earlier versions). +Images can be converted and uploaded via this but this requires Base64 image support +by BookStack so you'll need to be using BookStack v21.05.1 or greater. + +## Running + +First, download all the files in the same directory as this readme to a folder on your system +and run the below from within that directory. + +```bash +# Install NodeJS dependencies via NPM +npm install + +# Setup +# ALTERNATIVELY: Open the script and add to the empty strings in the variables at the top. +export BS_URL=https://bookstack.example.com # Set to be your BookStack base URL +export BS_TOKEN_ID=abc123 # Set to be your API token_id +export BS_TOKEN_SECRET=123abc # Set to be your API token_secret + +# Running the script +node index.js +``` + +- `` - File you want to convert & upload. +- `` - The unique book identifier shown in the URL bar within BookStack (Autogenerated from the name). + - For example: + - Book URL: https://example.com/books/bookstack-user-guide + - Book Slug: bookstack-user-guide + +## Examples + +```bash +# Convert the 'my_content.docx' file and upload to the Book with slug 'bookstack-user-guide' +node index.js my_content.docx bookstack-user-guide +``` \ No newline at end of file