From 453442d92f2a9a02b67b15b010bbb53d9f5acebd Mon Sep 17 00:00:00 2001 From: ChrisChrome Date: Sat, 19 Apr 2025 08:37:50 -0600 Subject: [PATCH] Add some docs --- index.js | 131 +++++++++++++++++++++++----- md/DOCS.md | 187 +++++++++++++++++++++++++++++++++++++++ package-lock.json | 208 +++++++++++++++++++++++++++++++++++++++++++- package.json | 4 +- public/index.html | 52 ----------- views/docViewer.ejs | 46 ++++++++++ 6 files changed, 550 insertions(+), 78 deletions(-) create mode 100644 md/DOCS.md delete mode 100644 public/index.html create mode 100644 views/docViewer.ejs diff --git a/index.js b/index.js index f798538..8c1da74 100644 --- a/index.js +++ b/index.js @@ -9,12 +9,38 @@ const blacklist = require("./blacklist.json") const wfos = require("./wfos.json") const events = require("./events.json") const os = require('os'); +const fs = require('fs'); +const path = require('path'); +const markdown = require('markdown-it')(); +const ejs = require('ejs'); const app = express(); expressWs(app); // Serve static files from the "public" directory app.use(express.static('public')); +app.use(express.json()); +app.use(express.urlencoded({ extended: true })); +app.set('view engine', 'ejs'); +app.set('views', path.join(__dirname, 'views')); + + +app.get("/", (req, res) => { + const markdownFilePath = path.join(__dirname, 'md', 'DOCS.md'); + + fs.readFile(markdownFilePath, 'utf8', (err, data) => { + if (err) { + console.error('Error reading markdown file:', err); + res.status(500).send('Internal Server Error'); + return; + } + + const htmlContent = markdown.render(data); + + res.render('docViewer', {title: 'Websocket Docs', htmlContent: htmlContent}); + }); +}); + global.wsConnections = []; var roomList = []; // IEM WebSocket @@ -36,7 +62,13 @@ app.ws('/iem', (ws, req) => { try { ws.on('message', (msg) => { const data = JSON.parse(msg); - if (!data.type) return; + if (!data.type) return ws.send(JSON.stringify({ + "type": "internal-response", + "code": 400, + "data": { + "error": "Invalid request." + } + })); switch (data.type) { case "subscribe": const subscriptionTarget = data.channel || getWFOroom(data.wfo) || "*"; @@ -129,6 +161,16 @@ app.ws('/iem', (ws, req) => { } })); } + break; + default: + ws.send(JSON.stringify({ + "type": "internal-response", + "code": 400, + "data": { + "error": "Invalid request." + } + })); + break; } }); } catch (error) { @@ -309,7 +351,7 @@ xmpp.on("offline", () => { }); var restartTimer = null; - +var sent = []; xmpp.on("stanza", (stanza) => { // Debug stuff //if (config.debug >= 2) console.log(`${colors.magenta("[DEBUG]")} Stanza: ${stanza.toString()}`); @@ -373,31 +415,71 @@ xmpp.on("stanza", (stanza) => { const diff = (now - product_id.timestamp) / 1000 / 60; if (diff > 3) return; // if (config.debug >= 1) console.log(`${colors.magenta("[DEBUG]")} New message from ${fromChannel}`); - console.log(`${colors.cyan("[INFO]")} ${getWFOByRoom(fromChannel).location} - ${evt.text} - ${product_id.timestamp}`); + console.log(`${colors.cyan("[INFO]")} ${getWFOByRoom(fromChannel).location} - ${product_id_raw} - ${evt.text} - ${product_id.timestamp}`); messages++; - - // Handle WebSocket - if (wsConnections.length > 0) { - wsConnections.forEach((connection) => { - if (connection.subs.includes(fromChannel) || connection.subs.includes("*")) { - connection.ws.send(JSON.stringify({ - "type": "iem-message", - "data": { - "channel": getWFOByRoom(fromChannel), - "event": evt, - "body": bodyData.string, - "timestamp": product_id.timestamp, - "wmo": product_id.wmo, - "pil": product_id.pil, - "station": product_id.station, - "raw": product_id_raw, - "rawBody": body, - "image": stanza.getChild("x").attrs.twitter_media || null + textTries = 0; + tryGetText = () => { + fetch(`https://mesonet.agron.iastate.edu/api/1/nwstext/${product_id_raw}`).then((res) => { + // If neither the body nor the product text contains the filter, ignore it + res.text().then((text) => { + if (wsConnections.length > 0) { + wsConnections.forEach((connection) => { + if (connection.subs.includes(fromChannel) || connection.subs.includes("*")) { + connection.ws.send(JSON.stringify({ + "type": "iem-message", + "data": { + "channel": getWFOByRoom(fromChannel), + "event": evt, + "body": bodyData.string, + "timestamp": product_id.timestamp, + "wmo": product_id.wmo, + "pil": product_id.pil, + "station": product_id.station, + "raw": product_id_raw, + "rawBody": body, + "image": stanza.getChild("x").attrs.twitter_media || null, + "productText": text || null + } + })); + } + }); + } + }); + }).catch((err) => { + setTimeout(() => { + if (textTries >= 3) { + console.log(`${colors.red("[ERROR]")} Failed to fetch product text, giving up... ${err}`) + if (wsConnections.length > 0) { + wsConnections.forEach((connection) => { + if (connection.subs.includes(fromChannel) || connection.subs.includes("*")) { + connection.ws.send(JSON.stringify({ + "type": "iem-message", + "data": { + "channel": getWFOByRoom(fromChannel), + "event": evt, + "body": bodyData.string, + "timestamp": product_id.timestamp, + "wmo": product_id.wmo, + "pil": product_id.pil, + "station": product_id.station, + "raw": product_id_raw, + "rawBody": body, + "image": stanza.getChild("x").attrs.twitter_media || null, + "productText": null + } + })); + } + }); } - })); - } + } else { + textTries++; + console.log(`${colors.red("[ERROR]")} Failed to fetch product text, retrying... ${err}`) + setTimeout(tryGetText, 100); + } + }) }); } + tryGetText(); } }); @@ -527,4 +609,5 @@ const PORT = process.env.SERVER_PORT || 3000; app.listen(PORT, () => { console.log(`Server is listening on ${PORT}`); start(); -}); \ No newline at end of file +}); + diff --git a/md/DOCS.md b/md/DOCS.md new file mode 100644 index 0000000..91a0dcb --- /dev/null +++ b/md/DOCS.md @@ -0,0 +1,187 @@ +# IEM Alerter WebSocket API Documentation + +WebSocket Endpoint: `/iem` + +This WebSocket endpoint allows clients to subscribe to weather-related channels, +receive real-time updates, and manage their subscriptions. + +## Legal Disclaimer + +This system is not intended to be used for, or as a supplement for information regarding life or death situations. For critical weather-related information, please rely on official sources such as National Weather Service radio stations, local radio or news outlets, and local emergency management agencies. + +## Connection +Upon connecting to the `/iem` WebSocket endpoint, the server will send a connection-info message +containing the following details: + +```json +{ + "type": "connection-info", + "state": true, + "uuid": "", + "host": "" +} +``` + +## Supported Message Types + +### 1. Subscribe to a Channel +Clients can subscribe to a specific channel or all channels. + +**Request:** +```json +{ + "type": "subscribe", + "channel": "" // Optional, subscribe to all channels if omitted +} +``` + +**Response:** +- Success: +```json +{ + "type": "internal-response", + "code": 200, + "data": { + "message": "Subscribed to " + } +} +``` +- Failure (Invalid Channel): +```json +{ + "type": "internal-response", + "code": 404, + "data": { + "error": "Invalid channel." + } +} +``` + +### 2. Unsubscribe from a Channel +Clients can unsubscribe from a specific channel or all channels. + +**Request:** +```json +{ + "type": "unsubscribe", + "channel": "" // Optional, unsubscribe from all channels if omitted +} +``` + +**Response:** +- Success: +```json +{ + "type": "internal-response", + "code": 200, + "data": { + "message": "Unsubscribed from " + } +} +``` +- Failure (Invalid Channel): +```json +{ + "type": "internal-response", + "code": 404, + "data": { + "error": "Invalid channel." + } +} +``` + +### 3. Get Current Subscriptions +Clients can request a list of their current subscriptions. + +**Request:** +```json +{ + "type": "get-subscriptions" +} +``` + +**Response:** +```json +{ + "type": "internal-response", + "code": 200, + "data": { + "subscriptions": ["", ""] + } +} +``` + +### 4. Get Room List +Clients can request a list of available rooms. + +**Request:** +```json +{ + "type": "room-list" +} +``` + +**Response:** +- Success: +```json +{ + "type": "room-list", + "code": 200, + "count": , + "data": ["", ""] +} +``` +- Failure (No Rooms Available): +```json +{ + "type": "room-list", + "code": 503, + "count": 0, + "data": { + "error": "Room list is currently empty. Please try again later." + } +} +``` + +## Real-Time Updates +When subscribed to a channel, clients will receive real-time updates in the following format: + +**Message:** +```json +{ + "type": "iem-message", + "data": { + "channel": { + "location": "", + "room": "" + }, + "event": { + "name": "", + "priority": , + "code": "" + }, + "body": "", + "timestamp": "", + "wmo": "", + "pil": "", + "station": "", + "raw": "", + "rawBody": "", + "image": "", // Optional + "productText": "" // Optional + } +} +``` +The Priority level is a number from 1 to 5, 1 being the loest priority and 5 being the highest. These priority levels are defined per alert type by the developers of the IEM alerter system. + +## Error Handling +If an error occurs, the server will send an error response: +```json +{ + "type": "internal-response", + "code": 500, + "data": { + "error": "Internal server error." + } +} +``` \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 28f81d8..4b5147d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,9 +12,11 @@ "@xmpp/client": "^0.13.4", "colors": "^1.4.0", "dotenv": "^16.5.0", + "ejs": "^3.1.10", "express": "^5.1.0", "express-ws": "^5.0.2", - "html-entities": "^2.6.0" + "html-entities": "^2.6.0", + "markdown-it": "^14.1.0" } }, "node_modules/@ampproject/remapping": { @@ -782,6 +784,21 @@ "node": ">= 0.6" } }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, "node_modules/anymatch": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", @@ -796,6 +813,12 @@ "node": ">= 8" } }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "license": "Python-2.0" + }, "node_modules/array-buffer-byte-length": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", @@ -855,6 +878,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "license": "MIT" + }, "node_modules/async-function": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", @@ -1091,6 +1120,22 @@ ], "license": "CC-BY-4.0" }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, "node_modules/chokidar": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", @@ -1143,6 +1188,24 @@ "node": ">=6" } }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, "node_modules/colors": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", @@ -1408,6 +1471,21 @@ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", "license": "MIT" }, + "node_modules/ejs": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", + "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", + "license": "Apache-2.0", + "dependencies": { + "jake": "^10.8.5" + }, + "bin": { + "ejs": "bin/cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/electron-to-chromium": { "version": "1.5.136", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.136.tgz", @@ -1423,6 +1501,18 @@ "node": ">= 0.8" } }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, "node_modules/es-abstract": { "version": "1.23.9", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.9.tgz", @@ -1690,6 +1780,36 @@ "node": "^12.20 || >= 14.13" } }, + "node_modules/filelist": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", + "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", + "license": "Apache-2.0", + "dependencies": { + "minimatch": "^5.0.1" + } + }, + "node_modules/filelist/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/filelist/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/fill-range": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", @@ -2001,6 +2121,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/has-property-descriptors": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", @@ -2565,6 +2694,24 @@ "node": ">=0.10.0" } }, + "node_modules/jake": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.2.tgz", + "integrity": "sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA==", + "license": "Apache-2.0", + "dependencies": { + "async": "^3.2.3", + "chalk": "^4.0.2", + "filelist": "^1.0.4", + "minimatch": "^3.1.2" + }, + "bin": { + "jake": "bin/cli.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -2610,6 +2757,15 @@ "integrity": "sha512-8ODW8TrDuMYvXRwra/Kh7/rJo9BtOfPc6qO8eAfC80CnCvSjSl0bkRM24X6/XBBEyj0v1nRUQ1LyOy3dbqOWXw==", "license": "MIT" }, + "node_modules/linkify-it": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==", + "license": "MIT", + "dependencies": { + "uc.micro": "^2.0.0" + } + }, "node_modules/locate-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", @@ -2663,6 +2819,23 @@ "semver": "bin/semver" } }, + "node_modules/markdown-it": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz", + "integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==", + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1", + "entities": "^4.4.0", + "linkify-it": "^5.0.0", + "mdurl": "^2.0.0", + "punycode.js": "^2.3.1", + "uc.micro": "^2.1.0" + }, + "bin": { + "markdown-it": "bin/markdown-it.mjs" + } + }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", @@ -2683,6 +2856,12 @@ "safe-buffer": "^5.1.2" } }, + "node_modules/mdurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==", + "license": "MIT" + }, "node_modules/media-typer": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", @@ -3089,6 +3268,15 @@ "node": ">= 0.10" } }, + "node_modules/punycode.js": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz", + "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/qs": { "version": "6.14.0", "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", @@ -3648,6 +3836,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -3758,6 +3958,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/uc.micro": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz", + "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==", + "license": "MIT" + }, "node_modules/unbox-primitive": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", diff --git a/package.json b/package.json index a5ec3d6..c055816 100644 --- a/package.json +++ b/package.json @@ -13,8 +13,10 @@ "@xmpp/client": "^0.13.4", "colors": "^1.4.0", "dotenv": "^16.5.0", + "ejs": "^3.1.10", "express": "^5.1.0", "express-ws": "^5.0.2", - "html-entities": "^2.6.0" + "html-entities": "^2.6.0", + "markdown-it": "^14.1.0" } } diff --git a/public/index.html b/public/index.html deleted file mode 100644 index c2de236..0000000 --- a/public/index.html +++ /dev/null @@ -1,52 +0,0 @@ - - - - - - - Document - - - -
- - - - \ No newline at end of file diff --git a/views/docViewer.ejs b/views/docViewer.ejs new file mode 100644 index 0000000..b83dcf2 --- /dev/null +++ b/views/docViewer.ejs @@ -0,0 +1,46 @@ + + + + + + <%= title %> + + + +
+ <%- htmlContent %> +
+ + \ No newline at end of file