diff --git a/README.md b/README.md index c8cf517..76b1925 100755 --- a/README.md +++ b/README.md @@ -40,37 +40,48 @@ The following Secrets and Variables must be configured under the "Actions" conte - REACT_APP_SITE_NAME ## API +### Variables + +| Variable | Value | +| ------------ | ------------ | +| ENABLE_CORS | Default is false, set to true when testing otherwise you will get CORS failures. In prod this should be handled by your proxy | +| RATE_LIMIT_S | The duration of your rate limit expressed in seconds | +RATE_LIMIT_MAX | How many requests a client can make in RATE_LIMIT_S before being blocked | +| FILE_SIZE_MB | How large of a file can be processed. This same size should be configured on your proxy for a more reliable failure. | + ### Local development On a Windows host with Docker installed and using Windows containers execute the following from inside the `api` directory to build and launch the [WinDebug-Container](https://github.com/PipeItToDevNull/WinDebug-Container) based API. ```bash -docker build -t api . ; docker run --rm -it -e ENABLE_CORS=true -p 3000:3000 api +docker build -t api . ; docker run --rm -it -e ENABLE_CORS=true -e FILE_SIZE_MB=15 -e RATE_LIMIT_S=60 -e RATE_LIMIT_MAX=10 -p 3001:3000 api ``` You may need process isolation if you get the error `hcs::CreateComputeSystem \\: The request is not supported.` ```bash -docker build --isolation=process -t api . ; docker run --isolation=process --rm -it -e ENABLE_CORS=true -p 3000:3000 api +docker build --isolation=process -t api . ; docker run --isolation=process --rm -it -e ENABLE_CORS=true -p 3001:3000 api ``` -Once launched use `REACT_APP_API_URL=http://localhost:3000` in your `.env` and launch your local development SWA. +Once launched use `REACT_APP_API_URL=http://localhost:3001` in your `.env` and launch your local development SWA. ### Deployment -Using an nginx reverse proxy to apply CORS (don't run ENABLE_CORS=true in prod) and SSL is the best method, the nginx default max client upload size of 10MB is fine for this appliction. +Using an nginx reverse proxy to apply CORS and SSL is the best method, the nginx default max client upload size of 10MB is fine for this appliction. You want to declare a volume for `C:\app\results`, an example command for deployment is below. ```bash -docker run -d --restart unless-stopped --name webdbg-api -v webdbg-results:C:\app\results -p 3000:3000 ghcr.io/r-techsupport/webdbg-api:latest +docker run -d --restart unless-stopped --name webdbg-api -v webdbg-results:C:\app\results -e FILE_SIZE_MB=10 -e RATE_LIMIT_S=60 -e RATE_LIMIT_MAX=5 -p 3000:3000 ghcr.io/r-techsupport/webdbg-api:latest ``` +You will want to increase the `proxy_read_timeout` if using nginx or the equivilant for your soltuion. Set it to at least 120s if not 300s. + ### PUT endpoint usage With a file ```bash curl.exe -X PUT http://localhost:3000/analyze-dmp -F "dmpFile=@path/to/test.dmp" ``` -With a URL +With a URL, if you have special characters in your URL like `&` you will need to encode it before submitting. ```bash curl -X PUT http://localhost:3000/analyze-dmp -F "url=http://example.com/file.dmp" ``` diff --git a/api/analyze.js b/api/analyze.js index f560b05..6fba69d 100644 --- a/api/analyze.js +++ b/api/analyze.js @@ -60,13 +60,25 @@ const processResult = (dmp, rawContent) => { const analysisLines = infos[1].split('\n').filter(line => !line.includes('*') && !line.includes("Debugging Details:")); const analysis = analysisLines.join('\n').trim(); - // Extracting bugcheck and arguments - const bugcheckMatch = analysis.match(/\(([^)]+)\)/); - const bugcheck = bugcheckMatch ? bugcheckMatch[1] : null; + // Extract human name and code, e.g. "MEMORY_MANAGEMENT (1a)" + // bugcheckHuman => "MEMORY_MANAGEMENT", bugcheck => "1a" + let bugcheckHuman = null; + let bugcheck = null; + const bcMatch = analysis.match(/^\s*([A-Za-z0-9 _-]+?)\s*\(\s*([0-9A-Fa-fx]+)\s*\)/m); + if (bcMatch) { + bugcheckHuman = bcMatch[1].trim(); + bugcheck = bcMatch[2].trim(); + } else { + // fallback: capture any code-like value in parentheses + const bugcheckOnlyMatch = analysis.match(/\(([0-9A-Fa-fx]+)\)/); + if (bugcheckOnlyMatch) bugcheck = bugcheckOnlyMatch[1].trim(); + } + + // Arguments like: Arg1: 00000000... + const argMatches = analysis.match(/Arg\d:\s*([0-9a-fA-Fx]+)/g); + const args = argMatches ? argMatches.map(arg => arg.split(':')[1].trim()) : []; - const argMatches = analysis.match(/Arg\d: ([0-9a-fA-Fx]+)/g); - const args = argMatches ? argMatches.map(arg => arg.split(': ')[1]) : []; - logger.info(`Bugcheck: ${bugcheck}, Args: ${args}`); + logger.info(`Bugcheck: ${bugcheck} (${bugcheckHuman || 'unknown'}), Args: ${args}`); // Output object creation const output = { @@ -74,6 +86,7 @@ const processResult = (dmp, rawContent) => { dmpInfo: dmpInfo, analysis: analysis, bugcheck: bugcheck, + bugcheckHuman: bugcheckHuman, args: args, rawContent: rawContent }; diff --git a/api/api.js b/api/api.js index c03f4db..24cf7e5 100755 --- a/api/api.js +++ b/api/api.js @@ -48,6 +48,28 @@ if (!fs.existsSync(resultsDir)) { fs.mkdirSync(resultsDir); } +// Read file size, rate limit window, and max hits from environment variables +const FILE_SIZE_MB = Number.isNaN(parseInt(process.env.FILE_SIZE_MB, 10)) + ? 10 + : parseInt(process.env.FILE_SIZE_MB, 10); // Default is 10 MB + +const RATE_LIMIT_S = Number.isNaN(parseInt(process.env.RATE_LIMIT_S, 10)) + ? 180 + : parseInt(process.env.RATE_LIMIT_S, 10); // Default 180 seconds (3 minutes) + +const RATE_LIMIT_MAX = Number.isNaN(parseInt(process.env.RATE_LIMIT_MAX, 10)) + ? 10 + : parseInt(process.env.RATE_LIMIT_MAX, 10); // Default 10 requests per window + +// Convert to bytes and milliseconds +const FILE_SIZE_BYTES = FILE_SIZE_MB * 1024 * 1024; +const RATE_LIMIT_MS = RATE_LIMIT_S * 1000; + +// Log the configured limits +logger.info(`File size limit: ${FILE_SIZE_MB}MB (${FILE_SIZE_BYTES} bytes)`); +logger.info(`Rate limit window: ${RATE_LIMIT_S} seconds (${RATE_LIMIT_MS} ms), max ${RATE_LIMIT_MAX} requests per window`); + + // Configure multer for file uploads const storage = multer.diskStorage({ destination: (req, file, cb) => { @@ -72,10 +94,10 @@ if (process.env.ENABLE_CORS === 'true') { }); } -// Size limit of 10M +// Size limit from environment variable (default 10MB) const upload = multer({ storage: storage, - limits: { fileSize: 10 * 1024 * 1024 } + limits: { fileSize: FILE_SIZE_BYTES } }); // Add security headers to all responses @@ -83,8 +105,8 @@ app.use(helmet()); // Add security headers to all responses // Rate limiting middleware to prevent abuse const limiter = rateLimit({ - windowMs: 3 * 60 * 1000, // 3 minutes - max: 10 // limit each IP to 10 requests per windowMs + windowMs: RATE_LIMIT_MS, // from environment variable + max: RATE_LIMIT_MAX // limit each IP to X requests per windowMs }); app.use(limiter); @@ -374,8 +396,8 @@ app.use(async (req, res, next) => { app.use((err, req, res, next) => { if (err instanceof multer.MulterError) { if (err.code === 'LIMIT_FILE_SIZE') { - logger.warn('File size exceeds the limit of 10MB'); - return res.status(400).send('File size exceeds the limit of 10MB'); + logger.warn(`File size exceeds the limit of ${FILE_SIZE_MB}MB`); + return res.status(400).send(`File size exceeds the limit of ${FILE_SIZE_MB}MB`); } } diff --git a/api/post-process.js b/api/post-process.js index ecd0ffb..6403fdd 100644 --- a/api/post-process.js +++ b/api/post-process.js @@ -1,5 +1,8 @@ import { exec } from 'child_process'; import winston from 'winston'; +import fs from 'fs'; +import path from 'path'; +import { fileURLToPath, pathToFileURL } from 'url'; // Configure Winston logger const logger = winston.createLogger({ @@ -14,15 +17,26 @@ const logger = winston.createLogger({ ] }); -// Configuration object for bugcheck commands -const bugcheckCommands = { - '9f': (parser, dmp, args) => `${parser} -z ${dmp} -c "k; !devstack ${args[1]} ; q"`, - '133': (parser, dmp) => `${parser} -z ${dmp} -c "k; !dpcwatchdog ; q"`, - // Add more bugcheck commands here as needed - // '': (dmp, args) => `${parser} -z ${dmp} -c "k; ; q"`, - // Args can be used in a command ${args[#]} - // Arg counts start at 0 so "Arg1" is ${args[0]} -}; +const bugcheckCommands = {}; +const processorsDir = path.join(path.dirname(fileURLToPath(import.meta.url)), 'post-processors'); + +try { + const files = fs.readdirSync(processorsDir).filter(f => f.endsWith('.js')); + for (const file of files) { + const name = path.basename(file, '.js'); + const filePath = path.join(processorsDir, file); + try { + // eslint-disable-next-line no-await-in-loop + const mod = await import(pathToFileURL(filePath).href); + bugcheckCommands[name] = mod.default; + logger.info(`Loaded post-processor: ${name} -> ${filePath}`); + } catch (err) { + logger.error(`Failed to load post-processor ${filePath}: ${err.stack || err}`); + } + } +} catch (err) { + logger.error(`Unable to read post-processors directory "${processorsDir}": ${err.stack || err}`); +} // Function to execute a command and return a promise const executeCommand = (command) => { @@ -42,8 +56,9 @@ const executeCommand = (command) => { // Function to perform additional operations on the analysis results const postProcessResults = async (results, parser) => { for (const result of results) { - const commandGenerator = bugcheckCommands[result.bugcheck]; + const commandGenerator = bugcheckCommands[String(result.bugcheck || '').toLowerCase()]; if (commandGenerator) { + // commandGenerator expected signature: (parser, dmp, args) => string const command = commandGenerator(parser, result.dmp, result.args); logger.info(`Executing command: ${command}`); try { @@ -55,7 +70,7 @@ const postProcessResults = async (results, parser) => { logger.error(`An error occured while post-processing the file: ${error}`); } } else { - result.post = "No post processing configured for this bugcheck" // Add a null post key if no command is run + result.post = "No post processing configured for this bugcheck"; logger.info(`No command for bugcheck: ${result.bugcheck}`); } } diff --git a/api/post-processors/133.js b/api/post-processors/133.js new file mode 100644 index 0000000..f025ae5 --- /dev/null +++ b/api/post-processors/133.js @@ -0,0 +1,4 @@ +// Generate command for bugcheck 133 (DPC_WATCHDOG_VIOLATION) +export default (parser, dmp) => { + return `${parser} -z ${dmp} -c "k; !dpcwatchdog ; q"`; +}; \ No newline at end of file diff --git a/api/post-processors/9f.js b/api/post-processors/9f.js new file mode 100644 index 0000000..e066f61 --- /dev/null +++ b/api/post-processors/9f.js @@ -0,0 +1,5 @@ +// Generate command for bugcheck 9f (PAGE_FAULT_IN_NONPAGED_AREA / example) +export default (parser, dmp, args = []) => { + const devstackArg = args[1] ? args[1] : ''; + return `${parser} -z ${dmp} -c "k; !devstack ${devstackArg} ; q"`; +}; \ No newline at end of file diff --git a/swa/package-lock.json b/swa/package-lock.json index 5525942..134b3ba 100644 --- a/swa/package-lock.json +++ b/swa/package-lock.json @@ -65,6 +65,7 @@ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.4.tgz", "integrity": "sha512-2BCOP7TN8M+gVDj7/ht3hsaO/B/n5oDbiAyyvnRlNOs+u1o+JWNYTQrmpuNp1/Wq2gcFrI01JAW+paEKDMx/CA==", "license": "MIT", + "peer": true, "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.3", @@ -714,6 +715,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.27.1.tgz", "integrity": "sha512-p9OkPbZ5G7UT1MofwYFigGebnrzGJacoBSQM0/6bi/PUMVE+qlWDD/OalvQKbwgQzU6dl0xAv6r4X7Jme0RYxA==", "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, @@ -1597,6 +1599,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.27.1.tgz", "integrity": "sha512-2KH4LWGSrJIkVf5tSiBFYuXDAoWRq2MMwgivCf+93dd0GQi8RXLjKA/0EvRnVV5G0hrHczsquXuD01L8s6dmBw==", "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.1", "@babel/helper-module-imports": "^7.27.1", @@ -3528,8 +3531,7 @@ "version": "5.0.4", "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", "integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@types/babel__core": { "version": "7.20.5", @@ -3902,6 +3904,7 @@ "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz", "integrity": "sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag==", "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/regexpp": "^4.4.0", "@typescript-eslint/scope-manager": "5.62.0", @@ -3955,6 +3958,7 @@ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.62.0.tgz", "integrity": "sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==", "license": "BSD-2-Clause", + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "5.62.0", "@typescript-eslint/types": "5.62.0", @@ -4324,6 +4328,7 @@ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -4422,6 +4427,7 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -5329,6 +5335,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "baseline-browser-mapping": "^2.8.9", "caniuse-lite": "^1.0.30001746", @@ -6696,8 +6703,7 @@ "version": "0.5.16", "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/dom-converter": { "version": "0.2.0", @@ -7184,6 +7190,7 @@ "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", @@ -9969,6 +9976,7 @@ "resolved": "https://registry.npmjs.org/jest/-/jest-27.5.1.tgz", "integrity": "sha512-Yn0mADZB89zTtjkPJEXwrac3LHudkQMR+Paqa8uxJHCBr9agxztUifWCyiYrjhMPBoUVBjyny0I7XH6ozDr7QQ==", "license": "MIT", + "peer": true, "dependencies": { "@jest/core": "^27.5.1", "import-local": "^3.0.2", @@ -10854,6 +10862,7 @@ "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz", "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", "license": "MIT", + "peer": true, "bin": { "jiti": "bin/jiti.js" } @@ -11247,7 +11256,6 @@ "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz", "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", "license": "MIT", - "peer": true, "bin": { "lz-string": "bin/bin.js" } @@ -12238,6 +12246,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", @@ -13372,6 +13381,7 @@ "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", "license": "MIT", + "peer": true, "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -13719,6 +13729,7 @@ "resolved": "https://registry.npmjs.org/react/-/react-19.2.0.tgz", "integrity": "sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ==", "license": "MIT", + "peer": true, "engines": { "node": ">=0.10.0" } @@ -13850,6 +13861,7 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.0.tgz", "integrity": "sha512-UlbRu4cAiGaIewkPyiRGJk0imDN2T3JjieT6spoL2UeSf5od4n5LB/mQ4ejmxhCFT1tYe8IvaFulzynWovsEFQ==", "license": "MIT", + "peer": true, "dependencies": { "scheduler": "^0.27.0" }, @@ -13904,6 +13916,7 @@ "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.11.0.tgz", "integrity": "sha512-F27qZr8uUqwhWZboondsPx8tnC3Ct3SxZA3V5WyEvujRyyNv0VYPhoBg1gZ8/MV5tubQp76Trw8lTv9hzRBa+A==", "license": "MIT", + "peer": true, "engines": { "node": ">=0.10.0" } @@ -14397,6 +14410,7 @@ "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.2.tgz", "integrity": "sha512-fS6iqSPZDs3dr/y7Od6y5nha8dW1YnbgtsyotCVvoFGKbERG++CVRFv1meyGDE1SNItQA8BrnCw7ScdAhRJ3XQ==", "license": "MIT", + "peer": true, "bin": { "rollup": "dist/bin/rollup" }, @@ -14639,6 +14653,7 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -15978,20 +15993,6 @@ } } }, - "node_modules/tailwindcss/node_modules/yaml": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.1.tgz", - "integrity": "sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==", - "license": "ISC", - "optional": true, - "peer": true, - "bin": { - "yaml": "bin.mjs" - }, - "engines": { - "node": ">= 14.6" - } - }, "node_modules/tapable": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz", @@ -16332,6 +16333,7 @@ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", "license": "(MIT OR CC0-1.0)", + "peer": true, "engines": { "node": ">=10" }, @@ -16761,6 +16763,7 @@ "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.102.1.tgz", "integrity": "sha512-7h/weGm9d/ywQ6qzJ+Xy+r9n/3qgp/thalBbpOi5i223dPXKi04IBtqPN9nTd+jBc7QKfvDbaBnFipYp4sJAUQ==", "license": "MIT", + "peer": true, "dependencies": { "@types/eslint-scope": "^3.7.7", "@types/estree": "^1.0.8", @@ -16832,6 +16835,7 @@ "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.15.2.tgz", "integrity": "sha512-0XavAZbNJ5sDrCbkpWL8mia0o5WPOd2YGtxrEiZkBK9FjLppIUK2TgxK6qGD2P3hUXTJNNPVibrerKcx5WkR1g==", "license": "MIT", + "peer": true, "dependencies": { "@types/bonjour": "^3.5.9", "@types/connect-history-api-fallback": "^1.3.5", @@ -17244,6 +17248,7 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", diff --git a/swa/src/App.js b/swa/src/App.js index 040a4fe..4500597 100644 --- a/swa/src/App.js +++ b/swa/src/App.js @@ -1,4 +1,4 @@ -import React, { useState } from 'react'; +import { useState } from 'react'; import { BrowserRouter as Router, Route, Routes, useNavigate } from 'react-router-dom'; import ResultPage from './ResultPage'; import { Helmet } from 'react-helmet'; @@ -107,84 +107,6 @@ const FileUpload = () => { } }; - // Removed unused isValidJson - - // Function to sort JSON keys - const sortJson = (data, order) => { - return order.reduce((acc, key) => { - if (data.hasOwnProperty(key)) { - acc[key] = data[key]; - } - return acc; - }, {}); - }; - - // Function to render JSON object into HTML objects - const renderJsonToHtml = (data) => { - console.log("Data received:", data); // Debugging line - - // No clue what this does, but everything is rendered inside it - if (Array.isArray(data)) { - return data.map((item) => { - // Use a stable key: stringified item or item.key if available - const key = (item && typeof item === 'object' && item.key) ? item.key : JSON.stringify(item); - return ( -
- {renderJsonToHtml(item)} -
- ); - }); - } - - // Define the key order to display in - const order = [ - "dmpName", - "dmpInfo", - "analysis", - "post", - "rawContent", - ]; - const specialKeys = ["rawContent"]; - const sortedData = sortJson(data, order); - - // Convert object to array of key-value pairs - const keyValueArray = Object.entries(sortedData).map(([key, value]) => ({ key, value })); - - // Separate the special items - const specialItems = keyValueArray.filter(item => specialKeys.includes(item.key)); - const regularItems = keyValueArray.filter(item => !specialKeys.includes(item.key)); - - // Render the regular items - const regularRender = regularItems.map((item) => ( - -

- {item.key} -

-
- {item.value} -
-
- )); - - // Render the special items with their own method - const specialRender = specialItems.map((item) => ( -
-
- Raw results -
{item.value}
-
-
- )); - - // Combine both renders - return ( - <> - {regularRender} - {specialRender} - - ); - }; - return (
@@ -214,7 +136,7 @@ const FileUpload = () => { {!error && !responseData &&

{loading ? 'Processing...' : 'Upload your .dmp file or a .zip file containing multiple .dmp files directly or via a direct link.'}

} {error &&

{error}

} {responseData && ( - <>{renderJsonToHtml(JSON.parse(responseData))} +
{responseData}
)}