tractatus/src/routes/research.routes.js
TheFlow ccb4bdaabf feat(api): implement research inquiry endpoint and Umami analytics
HIGH PRIORITY: Fixes production 404 error on research inquiry form

Research Inquiry API:
- Add POST /api/research-inquiry endpoint for form submissions
- Add admin endpoints for inquiry management (list, get, assign, respond, delete)
- Create ResearchInquiry model with MongoDB integration
- Add to moderation queue for human review (strategic quadrant)
- Include rate limiting (5 req/min) and CSRF protection
- Tested locally: endpoint responding, data saving to DB

Umami Analytics (Privacy-First):
- Add Docker Compose config for Umami + PostgreSQL
- Create nginx reverse proxy config with SSL support
- Implement privacy-first tracking script (DNT, opt-out, no cookies)
- Integrate tracking across 26 public HTML pages
- Exclude admin pages from tracking (privacy boundary)
- Add comprehensive deployment guide (UMAMI_SETUP_GUIDE.md)
- Environment variables added to .env.example

Files Created (9):
- src/models/ResearchInquiry.model.js
- src/controllers/research.controller.js
- src/routes/research.routes.js
- public/js/components/umami-tracker.js
- deployment-quickstart/nginx-analytics.conf
- deployment-quickstart/UMAMI_SETUP_GUIDE.md
- scripts/add-umami-tracking.sh
- scripts/add-tracking-python.py
- SESSION_SUMMARY_ANALYTICS_RESEARCH_INQUIRY.md

Files Modified (29):
- src/routes/index.js (research routes)
- deployment-quickstart/docker-compose.yml (umami services)
- deployment-quickstart/.env.example (umami config)
- 26 public HTML pages (tracking script)

Values Alignment:
 Privacy-First Design (cookie-free, DNT honored, opt-out available)
 Human Agency (research inquiries require human review)
 Data Sovereignty (self-hosted analytics, no third-party sharing)
 GDPR Compliance (no personal data in analytics)
 Transparency (open-source tools, documented setup)

Testing Status:
 Research inquiry: Locally tested, data verified in MongoDB
 Umami analytics: Pending production deployment

Next Steps:
1. Deploy to production (./scripts/deploy.sh)
2. Test research form on live site
3. Deploy Umami following UMAMI_SETUP_GUIDE.md
4. Update umami-tracker.js with website ID after setup

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-29 01:31:02 +13:00

86 lines
2.8 KiB
JavaScript

/**
* Research Inquiry Routes
* Academic research collaboration inquiry endpoints
*/
const express = require('express');
const router = express.Router();
const researchController = require('../controllers/research.controller');
const { authenticateToken, requireRole } = require('../middleware/auth.middleware');
const { validateRequired, validateEmail, validateObjectId } = require('../middleware/validation.middleware');
const { asyncHandler } = require('../middleware/error.middleware');
const { createInputValidationMiddleware } = require('../middleware/input-validation.middleware');
const { formRateLimiter } = require('../middleware/rate-limit.middleware');
/**
* Public routes
*/
// Validation schema for research inquiry submission
const researchInquirySchema = {
'name': { required: true, type: 'name', maxLength: 100 },
'email': { required: true, type: 'email', maxLength: 254 },
'institution': { required: true, type: 'default', maxLength: 200 },
'researchQuestion': { required: true, type: 'description', maxLength: 1000 },
'methodology': { required: true, type: 'description', maxLength: 1000 },
'context': { required: false, type: 'description', maxLength: 2000 },
'needs': { required: false, type: 'array' },
'otherNeeds': { required: false, type: 'description', maxLength: 500 },
'timeline': { required: false, type: 'default', maxLength: 100 }
};
// POST /api/research-inquiry - Submit research inquiry (public)
router.post('/',
formRateLimiter, // 5 requests per minute
createInputValidationMiddleware(researchInquirySchema),
validateRequired(['name', 'email', 'institution', 'researchQuestion', 'methodology']),
validateEmail('email'),
asyncHandler(researchController.submitInquiry)
);
/**
* Admin routes
*/
// GET /api/research-inquiry - List all inquiries (admin)
router.get('/',
authenticateToken,
requireRole('admin', 'moderator'),
asyncHandler(researchController.listInquiries)
);
// GET /api/research-inquiry/:id - Get inquiry by ID (admin)
router.get('/:id',
authenticateToken,
requireRole('admin', 'moderator'),
validateObjectId('id'),
asyncHandler(researchController.getInquiry)
);
// POST /api/research-inquiry/:id/assign - Assign inquiry to user (admin)
router.post('/:id/assign',
authenticateToken,
requireRole('admin'),
validateObjectId('id'),
asyncHandler(researchController.assignInquiry)
);
// POST /api/research-inquiry/:id/respond - Mark as responded (admin)
router.post('/:id/respond',
authenticateToken,
requireRole('admin', 'moderator'),
validateObjectId('id'),
validateRequired(['content']),
asyncHandler(researchController.respondToInquiry)
);
// DELETE /api/research-inquiry/:id - Delete inquiry (admin)
router.delete('/:id',
authenticateToken,
requireRole('admin'),
validateObjectId('id'),
asyncHandler(researchController.deleteInquiry)
);
module.exports = router;