Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,12 @@ Once launched use `REACT_APP_API_URL=http://localhost:3000` in your `.env` and l
### 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.

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
```

### PUT endpoint usage
With a file
```bash
Expand Down
3 changes: 3 additions & 0 deletions api/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ COPY . .
# Install application dependencies
RUN npm install

# Declare a volume for results so they persist
VOLUME C:\\app\\results

# Expose the application port
EXPOSE 3000

Expand Down
7 changes: 6 additions & 1 deletion api/USAGE.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# BSOD-API

A basic API to injest `.dmp` files and return analyzed text.
A basic API to injest `.dmp` files and return a UUID in JSON associated with the results. Fetching the UUID will return the associated result in JSON.

## Usage
With a file
Expand All @@ -11,4 +11,9 @@ curl.exe -X PUT http://localhost:3000/analyze-dmp -F "dmpFile=@path/to/test.dmp"
With a URL
```bash
curl -X PUT http://localhost:3000/analyze-dmp -F "url=http://example.com/file.dmp"
```

Retrieve result JSON
```bash
curl -X GET http://chakotay.dev0.sh:3001/<uuid>
```
70 changes: 67 additions & 3 deletions api/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,12 @@ if (!fs.existsSync(uploadsDir)) {
fs.mkdirSync(uploadsDir);
}

// Esure the results directory exists
const resultsDir = path.join(__dirname, 'results');
if (!fs.existsSync(resultsDir)) {
fs.mkdirSync(resultsDir);
}

// Configure multer for file uploads
const storage = multer.diskStorage({
destination: (req, file, cb) => {
Expand Down Expand Up @@ -111,14 +117,21 @@ const checkFileHeader = (checkPath) => {
return true;
};

// Function to execute analysis commands on local files
// Function to execute analysis commands on local files and save result as JSON
const analyzeFile = async (filePath, res) => {
logger.info(`Sending target: ${filePath} for analysis`);

try {
const analysisResult = await Analyze(filePath);
logger.info('Analysis output sent to client');
res.json(JSON.parse(analysisResult));
const resultObj = JSON.parse(analysisResult);
// Generate short result ID (first octet of UUID)
const resultId = uuidv4().split('-')[0];
const resultPath = path.join(resultsDir, `${resultId}.json`);
// Save result to file
await fs.promises.writeFile(resultPath, JSON.stringify(resultObj, null, 2), 'utf8');
logger.info(`Analysis result saved: ${resultPath}`);
// Return only the UUID to the client
res.json({ uuid: resultId });
} catch (error) {
logger.error(`Failed to analyze target: ${error.message}`);
res.status(500).send("An error occurred while analyzing the file");
Expand All @@ -127,6 +140,25 @@ const analyzeFile = async (filePath, res) => {
}
};

// GET endpoint to fetch analysis result by UUID
app.get('/:uuid', async (req, res) => {
const { uuid } = req.params;
const resultPath = path.join(resultsDir, `${uuid}.json`);
logger.info(`Result requested: ${resultPath}`);
try {
if (!fs.existsSync(resultPath)) {
logger.info(`Result not found: ${resultPath}`);
return res.status(404).send('Result not found');
}
const data = await fs.promises.readFile(resultPath, 'utf8');
logger.info(`Result served: ${resultPath}`);
res.type('application/json').send(data);
} catch (err) {
logger.error(`Failed to fetch result for UUID ${uuid}: ${err.message}`);
res.status(500).send('An error occured while retrieving result');
}
});

// PUT and POST endpoint to receive .dmp file or URL and analyze it
const handleAnalyzeDmp = async (req, res) => {
const uploadName = req.uploadName || uuidv4().split('-')[0]; // Retrieve the upload name from the request object
Expand Down Expand Up @@ -306,6 +338,38 @@ app.get('/', (req, res) => {
});
});

// Cleanup function to delete result files older than 7 days
const cleanupOldResults = async () => {
const now = Date.now();
const weekMs = 7 * 24 * 60 * 60 * 1000;
try {
const files = await fs.promises.readdir(resultsDir);
let deletedCount = 0;
for (const file of files) {
if (file.endsWith('.json')) {
const filePath = path.join(resultsDir, file);
const stat = await fs.promises.stat(filePath);
if (now - stat.mtimeMs > weekMs) {
await fs.promises.unlink(filePath);
logger.info(`Deleted old result file: ${filePath}`);
deletedCount++;
}
}
}
if (deletedCount === 0) {
logger.info('Cleanup ran: no old result files deleted.');
}
} catch (err) {
logger.error(`Cleanup error: ${err.message}`);
}
};

// Cleanup middleware: run after every request
app.use(async (req, res, next) => {
await cleanupOldResults();
next();
});

// Centralized error handling middleware
app.use((err, req, res, next) => {
if (err instanceof multer.MulterError) {
Expand Down
62 changes: 58 additions & 4 deletions swa/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

17 changes: 9 additions & 8 deletions swa/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,15 @@
"version": "0.1.0",
"private": true,
"dependencies": {
"@testing-library/jest-dom": "^6.9.1",
"@testing-library/react": "^16.3.0",
"@testing-library/user-event": "^14.6.1",
"react": "^19.2.0",
"react-dom": "^19.2.0",
"react-helmet": "^6.1.0",
"react-scripts": "5.0.1",
"web-vitals": "^5.1.0"
"@testing-library/jest-dom": "^6.9.1",
"@testing-library/react": "^16.3.0",
"@testing-library/user-event": "^14.6.1",
"react": "^19.2.0",
"react-dom": "^19.2.0",
"react-helmet": "^6.1.0",
"react-router-dom": "^7.9.4",
"react-scripts": "5.0.1",
"web-vitals": "^5.1.0"
},
"scripts": {
"start": "react-scripts start",
Expand Down
Loading
Loading