]> BookStack Code Mirror - api-scripts/commitdiff
Added node docx-to-page example
authorDan Brown <redacted>
Fri, 4 Jun 2021 23:28:08 +0000 (00:28 +0100)
committerDan Brown <redacted>
Fri, 4 Jun 2021 23:28:08 +0000 (00:28 +0100)
node-docx-to-page/index.js [new file with mode: 0644]
node-docx-to-page/package-lock.json [new file with mode: 0644]
node-docx-to-page/package.json [new file with mode: 0644]
node-docx-to-page/readme.md [new file with mode: 0644]

diff --git a/node-docx-to-page/index.js b/node-docx-to-page/index.js
new file mode 100644 (file)
index 0000000..ca2fb85
--- /dev/null
@@ -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 <docx_file> and <book_slug> 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 (file)
index 0000000..8ea6be1
--- /dev/null
@@ -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 (file)
index 0000000..43e27e0
--- /dev/null
@@ -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 (file)
index 0000000..adedb82
--- /dev/null
@@ -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 <docx_file> <book_slug>
+```
+
+- `<docx_file>` - File you want to convert & upload.
+- `<book_slug>` - 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