tractatus/src/routes/test.routes.js
TheFlow 2298d36bed fix(submissions): restructure Economist package and fix article display
- Create Economist SubmissionTracking package correctly:
  * mainArticle = full blog post content
  * coverLetter = 216-word SIR— letter
  * Links to blog post via blogPostId
- Archive 'Letter to The Economist' from blog posts (it's the cover letter)
- Fix date display on article cards (use published_at)
- Target publication already displaying via blue badge

Database changes:
- Make blogPostId optional in SubmissionTracking model
- Economist package ID: 68fa85ae49d4900e7f2ecd83
- Le Monde package ID: 68fa2abd2e6acd5691932150

Next: Enhanced modal with tabs, validation, export

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-24 08:47:42 +13:00

110 lines
2.9 KiB
JavaScript

/**
* Test Routes
* Development and testing endpoints
*/
const express = require('express');
const router = express.Router();
const { createSecureUpload, ALLOWED_MIME_TYPES } = require('../middleware/file-security.middleware');
const { asyncHandler } = require('../middleware/error.middleware');
const logger = require('../utils/logger.util');
/**
* Test file upload endpoint
* POST /api/test/upload
*
* Tests the complete file security pipeline:
* - Multer upload
* - MIME type validation
* - Magic number validation
* - ClamAV malware scanning
* - Quarantine system
*/
router.post('/upload',
...createSecureUpload({
fileType: 'document',
maxFileSize: 10 * 1024 * 1024, // 10MB
allowedMimeTypes: ALLOWED_MIME_TYPES.document,
fieldName: 'file'
}),
asyncHandler(async (req, res) => {
if (!req.file) {
return res.status(400).json({
error: 'Bad Request',
message: 'No file uploaded'
});
}
logger.info(`Test file upload successful: ${req.file.originalname}`);
res.json({
success: true,
message: 'File uploaded and validated successfully',
file: {
originalName: req.file.originalname,
filename: req.file.filename,
mimetype: req.file.mimetype,
size: req.file.size,
path: req.file.path
},
security: {
mimeValidated: true,
malwareScan: 'passed',
quarantined: false
}
});
})
);
/**
* Get upload statistics
* GET /api/test/upload-stats
*/
router.get('/upload-stats',
asyncHandler(async (req, res) => {
const fs = require('fs').promises;
const path = require('path');
try {
const uploadDir = process.env.UPLOAD_DIR || '/tmp/tractatus-uploads';
const quarantineDir = process.env.QUARANTINE_DIR || '/var/quarantine/tractatus';
const uploadFiles = await fs.readdir(uploadDir).catch(() => []);
const quarantineFiles = await fs.readdir(quarantineDir).catch(() => []);
// Get quarantine details
const quarantineDetails = [];
for (const file of quarantineFiles) {
if (file.endsWith('.json')) {
const metadataPath = path.join(quarantineDir, file);
const metadata = JSON.parse(await fs.readFile(metadataPath, 'utf8'));
quarantineDetails.push(metadata);
}
}
res.json({
success: true,
stats: {
uploads: {
directory: uploadDir,
count: uploadFiles.length,
files: uploadFiles
},
quarantine: {
directory: quarantineDir,
count: Math.floor(quarantineFiles.length / 2), // Each quarantined file has .json metadata
items: quarantineDetails
}
}
});
} catch (error) {
logger.error('Upload stats error:', error);
res.status(500).json({
error: 'Internal Server Error',
message: 'Failed to retrieve upload statistics'
});
}
})
);
module.exports = router;