tractatus/src/routes/documents.routes.js
TheFlow 426fde1ac5 feat(infra): semantic versioning and systemd service implementation
**Cache-Busting Improvements:**
- Switched from timestamp-based to semantic versioning (v1.0.2)
- Updated all HTML files: index.html, docs.html, leader.html
- CSS: tailwind.css?v=1.0.2
- JS: navbar.js, document-cards.js, docs-app.js v1.0.2
- Professional versioning approach for production stability

**systemd Service Implementation:**
- Created tractatus-dev.service for development environment
- Created tractatus-prod.service for production environment
- Added install-systemd.sh script for easy deployment
- Security hardening: NoNewPrivileges, PrivateTmp, ProtectSystem
- Resource limits: 1GB dev, 2GB prod memory limits
- Proper logging integration with journalctl
- Automatic restart on failure (RestartSec=10)

**Why systemd over pm2:**
1. Native Linux integration, no additional dependencies
2. Better OS-level security controls (ProtectSystem, ProtectHome)
3. Superior logging with journalctl integration
4. Standard across Linux distributions
5. More robust process management for production

**Usage:**
  # Development:
  sudo ./scripts/install-systemd.sh dev

  # Production:
  sudo ./scripts/install-systemd.sh prod

  # View logs:
  sudo journalctl -u tractatus -f

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-09 09:16:22 +13:00

70 lines
1.7 KiB
JavaScript

/**
* Documents Routes
* Framework documentation endpoints
*/
const express = require('express');
const router = express.Router();
const documentsController = require('../controllers/documents.controller');
const { authenticateToken, requireRole } = require('../middleware/auth.middleware');
const { validateRequired, validateObjectId, validateSlug } = require('../middleware/validation.middleware');
const { asyncHandler } = require('../middleware/error.middleware');
/**
* Public routes (read-only)
*/
// GET /api/documents/search?q=query
router.get('/search',
asyncHandler(documentsController.searchDocuments)
);
// GET /api/documents
router.get('/', (req, res, next) => {
// Redirect browser requests to API documentation
const acceptsHtml = req.accepts('html');
const acceptsJson = req.accepts('json');
if (acceptsHtml && !acceptsJson) {
return res.redirect(302, '/api-reference.html#documents');
}
next();
}, asyncHandler(documentsController.listDocuments));
// GET /api/documents/:identifier (ID or slug)
router.get('/:identifier',
asyncHandler(documentsController.getDocument)
);
/**
* Admin routes (protected)
*/
// POST /api/documents
router.post('/',
authenticateToken,
requireRole('admin'),
validateRequired(['title', 'slug', 'quadrant', 'content_markdown']),
validateSlug,
asyncHandler(documentsController.createDocument)
);
// PUT /api/documents/:id
router.put('/:id',
authenticateToken,
requireRole('admin'),
validateObjectId('id'),
asyncHandler(documentsController.updateDocument)
);
// DELETE /api/documents/:id
router.delete('/:id',
authenticateToken,
requireRole('admin'),
validateObjectId('id'),
asyncHandler(documentsController.deleteDocument)
);
module.exports = router;