diff --git a/SETUP_INSTRUCTIONS.md b/SETUP_INSTRUCTIONS.md new file mode 100644 index 00000000..fa8db97e --- /dev/null +++ b/SETUP_INSTRUCTIONS.md @@ -0,0 +1,277 @@ +# Tractatus Infrastructure Setup - Terminal Commands + +**Run these commands in a separate terminal window** +**Current directory:** `/home/theflow/projects/tractatus` + +--- + +## Step 1: Install Node.js Dependencies + +```bash +cd /home/theflow/projects/tractatus +npm install +``` + +**Expected output:** Installation of ~20 packages (express, mongodb, jwt, etc.) +**Time:** ~30-60 seconds + +--- + +## Step 2: Create Environment Configuration + +```bash +cp .env.example .env +``` + +**Optional:** Edit .env if you need custom settings (defaults are fine for development) + +```bash +nano .env # or use your preferred editor +``` + +The defaults are: +- `PORT=9000` +- `MONGODB_URI=mongodb://localhost:27017/tractatus_dev` +- `NODE_ENV=development` + +**Press Ctrl+X to exit nano if you opened it** + +--- + +## Step 3: Install MongoDB Systemd Service + +```bash +cd /home/theflow/projects/tractatus/scripts +sudo ./install-mongodb-service.sh +``` + +**Expected output:** +``` +Installing MongoDB Tractatus systemd service... +Copying service file to /etc/systemd/system... +Reloading systemd daemon... +Enabling service to start on boot... +MongoDB Tractatus service installed successfully! +``` + +**You will be prompted for your sudo password** + +--- + +## Step 4: Start MongoDB Service + +```bash +sudo systemctl start mongodb-tractatus +``` + +**Verify it's running:** + +```bash +sudo systemctl status mongodb-tractatus +``` + +**Expected output:** `Active: active (running)` in green + +**Check the logs:** + +```bash +tail -f /home/theflow/projects/tractatus/logs/mongodb.log +``` + +**You should see:** MongoDB startup messages +**Press Ctrl+C to exit tail** + +--- + +## Step 5: Verify MongoDB Port + +```bash +lsof -i :27017 +``` + +**Expected output:** +``` +COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME +mongod xxxxx theflow xx IPv4 xxxxxx 0t0 TCP localhost:27017 (LISTEN) +``` + +**Verify it's on port 27017, NOT 27027 (family-history)** + +--- + +## Step 6: Initialize Database + +```bash +cd /home/theflow/projects/tractatus +npm run init:db +``` + +**Expected output:** +``` +πŸš€ Starting Tractatus database initialization... +βœ… Connected to MongoDB +πŸ“¦ Processing collection: documents + βœ“ Created collection + βœ“ Created index: slug_1 + ... +✨ Database initialization complete! +``` + +**This creates 10 collections with indexes** + +--- + +## Step 7: Verify Database Setup + +```bash +mongosh mongodb://localhost:27017/tractatus_dev --eval "db.getCollectionNames()" +``` + +**Expected output:** Array of 10 collection names + +**Alternative verification:** + +```bash +mongosh mongodb://localhost:27017/tractatus_dev +``` + +Then in the MongoDB shell: +```javascript +show collections +db.documents.getIndexes() +exit +``` + +--- + +## Step 8: Check Application Readiness + +```bash +cd /home/theflow/projects/tractatus +npm run dev +``` + +**Expected:** Server will fail because we haven't built `src/server.js` yet +**This is normal!** Claude Code is building it now. + +**Press Ctrl+C to stop if it started** + +--- + +## Troubleshooting + +### MongoDB won't start? + +**Check if port 27017 is already in use:** +```bash +lsof -i :27017 +``` + +**If something else is using it, we'll use port 27029 instead:** +```bash +# Edit the service file +sudo nano /etc/systemd/system/mongodb-tractatus.service +# Change all instances of 27017 to 27029 +# Save and exit + +# Reload and restart +sudo systemctl daemon-reload +sudo systemctl restart mongodb-tractatus +``` + +### Permission errors? + +**Ensure MongoDB data directory is owned by your user:** +```bash +sudo chown -R theflow:theflow /home/theflow/projects/tractatus/data/mongodb +sudo chown -R theflow:theflow /home/theflow/projects/tractatus/logs +``` + +### npm install fails? + +**Check Node.js version (must be 18+):** +```bash +node --version +``` + +**If < 18, update Node.js first** + +--- + +## Infrastructure Status Checklist + +After completing steps above, verify: + +- [ ] `npm install` completed successfully +- [ ] `.env` file created +- [ ] MongoDB systemd service installed +- [ ] MongoDB service running on port 27017 +- [ ] Database initialized (10 collections created) +- [ ] No port conflicts with family-history (27027) + +--- + +## What Claude Code is Building Now + +While you run these commands, Claude Code is: + +1. βœ… Adapting governance documents (TRA-VAL-0001 from STR-VAL-0001) +2. βœ… Building database utilities and models +3. βœ… Creating Express server foundation +4. βœ… Implementing Tractatus governance services +5. βœ… Building core API routes +6. ⏳ Will continue with features... + +--- + +## When Setup is Complete + +**Run this to verify everything:** + +```bash +cd /home/theflow/projects/tractatus +npm run dev +``` + +**Expected output:** +``` +πŸš€ Tractatus server starting... +βœ… Connected to MongoDB: tractatus_dev +βœ… Server listening on port 9000 +✨ Ready for development +``` + +**Open browser:** http://localhost:9000 (will show basic response) + +--- + +## Service Management Commands + +**Start MongoDB:** +```bash +sudo systemctl start mongodb-tractatus +``` + +**Stop MongoDB:** +```bash +sudo systemctl stop mongodb-tractatus +``` + +**Restart MongoDB:** +```bash +sudo systemctl restart mongodb-tractatus +``` + +**Check status:** +```bash +sudo systemctl status mongodb-tractatus +``` + +**View logs:** +```bash +sudo journalctl -u mongodb-tractatus -f +``` + +--- + +**Questions?** Check CLAUDE.md or ask during next session. diff --git a/docs/governance/TRA-VAL-0001-core-values-principles-v1-0.md b/docs/governance/TRA-VAL-0001-core-values-principles-v1-0.md new file mode 100644 index 00000000..ad8a5ca2 --- /dev/null +++ b/docs/governance/TRA-VAL-0001-core-values-principles-v1-0.md @@ -0,0 +1,302 @@ +# Tractatus AI Safety Framework - Core Values and Principles + +**Document Type:** Strategic Foundation +**Created:** 2025-10-06 +**Author:** John Stroh +**Version:** 1.0 +**Status:** Active +**Filename:** TRA-VAL-0001-core-values-principles-v1-0.md +**Document Code:** TRA-VAL-0001 +**Directory Path:** docs/governance/ +**Security Classification:** Public + +**Primary Quadrant:** STRATEGIC +**Related Quadrants:** OPS, TAC, SYS + +**Implements:** Tractatus Framework Specification v2.0 +**Implements Relationship:** Foundation +**Related Documents:** TRA-GOV-0001, TRA-GOV-0002, TRA-GOV-0003 +**Related Relationship:** Core +**Implementation Status:** Active + +--- + +## Purpose + +This document establishes the foundational values and principles that guide the Tractatus AI Safety Framework and all aspects of this website platform. These enduring elements represent our deepest commitments to safe AI development and provide the basis for strategic alignment across all features, content, and operations. + +--- + +## Core Values + +### Sovereignty & Self-determination +- **Human Agency Preservation**: AI systems must augment, never replace, human decision-making authority +- **User Control**: Individuals maintain complete control over their data and engagement with AI features +- **No Manipulation**: Zero dark patterns, no hidden AI influence, complete transparency in AI operations +- **Explicit Consent**: All AI features require clear user understanding and opt-in + +### Transparency & Honesty +- **Visible AI Reasoning**: All AI-generated suggestions include the reasoning process +- **Public Moderation Queue**: Human oversight decisions are documented and visible +- **Clear Boundaries**: Explicitly communicate what AI can and cannot do +- **Honest Limitations**: Acknowledge framework limitations and edge cases +- **No Proprietary Lock-in**: Open source, open standards, exportable data + +### Harmlessness & Protection +- **Privacy-First Design**: No tracking, no surveillance, minimal data collection +- **Security by Default**: Regular audits, penetration testing, zero-trust architecture +- **Fail-Safe Mechanisms**: AI errors default to human review, not automatic action +- **Boundary Enforcement**: Architectural guarantees prevent AI from making values decisions +- **User Safety**: Protection from AI-generated misinformation or harmful content + +### Human Judgment Primacy +- **Values Decisions**: Always require human approval, never delegated to AI +- **Strategic Oversight**: Human authority over mission, values, and governance +- **Escalation Protocols**: Clear pathways for AI to request human guidance +- **Override Capability**: Humans can always override AI suggestions +- **Accountability**: Human responsibility for all AI-assisted actions + +### Community & Accessibility +- **Universal Access**: Core framework documentation freely available to all +- **Three Audience Paths**: Tailored content for Researchers, Implementers, Advocates +- **Economic Accessibility**: Free tier with substantive capabilities +- **Knowledge Sharing**: Open collaboration, peer review, community contributions +- **WCAG Compliance**: Accessible to all abilities and assistive technologies + +### Biodiversity & Ecosystem Thinking +- **Multiple Valid Approaches**: No single solution, respect for alternative frameworks +- **Interoperability**: Integration with diverse AI safety approaches +- **Sustainability**: Long-term viability over short-term growth +- **Resilience**: Distributed systems, multiple mirrors, no single points of failure +- **Environmental Responsibility**: Green hosting, efficient code, minimal resource consumption + +--- + +## Guiding Principles + +### Architectural Safety Guarantees +- **Structural over Training**: Safety through architecture, not just fine-tuning +- **Explicit Boundaries**: Codified limits on AI action authority +- **Verifiable Compliance**: Automated checks against strategic values +- **Cross-Reference Validation**: AI actions validated against explicit instructions +- **Context Pressure Monitoring**: Detection of error-prone conditions + +### Dogfooding Implementation +- **Self-Application**: This website uses Tractatus to govern its own AI operations +- **Living Demonstration**: Platform proves framework effectiveness through use +- **Continuous Validation**: Real-world testing of governance mechanisms +- **Transparent Meta-Process**: Public documentation of how AI governs AI + +### Progressive Implementation +- **Phased Rollout**: 4-phase deployment over 18 months +- **Incremental Features**: Add capabilities as governance matures +- **No Shortcuts**: Quality over speed, world-class execution +- **Learn and Adapt**: Iterate based on real-world feedback + +### Education-Centered Approach +- **Demystify AI Safety**: Make complex concepts accessible +- **Build Literacy**: Empower users to understand AI governance +- **Interactive Demonstrations**: Learn by doing (classification, 27027 incident, boundary enforcement) +- **Case Study Learning**: Real-world failures and successes +- **Open Research**: Share findings, encourage replication + +### Jurisdictional Awareness & Data Sovereignty +- **Respect Indigenous Leadership**: Honor indigenous data sovereignty principles (CARE Principles) +- **Te Tiriti Foundation**: Acknowledge Te Tiriti o Waitangi as strategic baseline +- **Location-Aware Hosting**: Consider data residency and jurisdiction +- **Global Application**: Framework designed for worldwide implementation +- **Local Adaptation**: Support for cultural and legal contexts + +### AI Governance Framework +- **Quadrant-Based Classification**: Strategic/Operational/Tactical/System/Stochastic organization +- **Time-Persistence Metadata**: Instructions classified by longevity and importance +- **Human-AI Collaboration**: Clear delineation of authority and responsibility +- **Instruction Persistence**: Critical directives maintained across context windows +- **Metacognitive Verification**: AI self-assessment before proposing actions + +### Research & Validation Priority +- **Peer Review**: Academic rigor, scholarly publication +- **Reproducible Results**: Open code, documented methodologies +- **Falsifiability**: Framework designed to be tested and potentially disproven +- **Continuous Research**: Ongoing validation and refinement +- **Industry Collaboration**: Partnerships with AI organizations + +### Sustainable Operations +- **Koha Model**: Transparent, community-supported funding (Phase 3+) +- **No Exploitation**: Fair pricing, clear value exchange +- **Resource Efficiency**: Optimized code, cached content, minimal overhead +- **Long-Term Thinking**: Decades, not quarters +- **Community Ownership**: Contributors have stake in success + +--- + +## Te Tiriti o Waitangi Commitment + +**Strategic Baseline (Not Dominant Cultural Overlay):** + +The Tractatus framework acknowledges **Te Tiriti o Waitangi** and indigenous leadership in digital sovereignty as a strategic foundation for this work. We: + +- **Respect Indigenous Data Sovereignty**: Follow documented principles (CARE Principles, Te Mana Raraunga research) +- **Acknowledge Historical Leadership**: Indigenous peoples have led sovereignty struggles for centuries +- **Apply Published Standards**: Use peer-reviewed indigenous data governance frameworks +- **Defer Deep Engagement**: Will not approach Māori organizations until we have something valuable to offer + +**Implementation:** +- Footer acknowledgment (subtle, respectful) +- `/about/values` page (detailed explanation) +- Resource directory (links to Māori data sovereignty work) +- No tokenism, no performative gestures + +--- + +## Values Alignment in Practice + +### Content Curation (Blog, Resources) +- **AI Suggests**: Claude analyzes trends, proposes topics +- **Human Approves**: All values-sensitive content requires human review +- **Transparency**: AI reasoning visible in moderation queue +- **Attribution**: Clear "AI-curated, human-approved" labels + +### Media Inquiries +- **AI Triages**: Analyzes urgency, topic sensitivity +- **Human Responds**: All responses written or approved by humans +- **Escalation**: Values-sensitive topics immediately escalated to strategic review + +### Case Study Submissions +- **AI Reviews**: Assesses relevance, completeness +- **Human Validates**: Final publication decision always human +- **Quality Control**: Framework alignment checked against TRA-VAL-0001 + +### Interactive Demonstrations +- **Educational Purpose**: Teach framework concepts through interaction +- **No Live Data**: Demonstrations use example scenarios only +- **Transparency**: Show exactly how classification and validation work + +--- + +## Decision Framework + +When values conflict (e.g., transparency vs. privacy, speed vs. safety): + +1. **Explicit Recognition**: Acknowledge the tension publicly +2. **Context Analysis**: Consider specific situation and stakeholders +3. **Hierarchy Application**: + - Human Safety > System Performance + - Privacy > Convenience + - Transparency > Proprietary Advantage + - Long-term Sustainability > Short-term Growth +4. **Document Resolution**: Record decision rationale for future reference +5. **Community Input**: Seek feedback on significant value trade-offs + +--- + +## Review and Evolution + +### Annual Review Process +- **Scheduled:** 2026-10-06 (one year from creation) +- **Scope:** Comprehensive evaluation of values relevance and implementation +- **Authority:** Human PM (John Stroh) with community input +- **Outcome:** Updated version or reaffirmation of current values + +### Triggering Extraordinary Review +Immediate review required if: +- Framework fails to prevent significant AI harm +- Values found to be in conflict with actual operations +- Major regulatory or ethical landscape changes +- Community identifies fundamental misalignment + +### Evolution Constraints +- Core values (Sovereignty, Transparency, Harmlessness, Human Judgment) are **immutable** +- Guiding principles may evolve based on evidence and experience +- Changes require explicit human approval and public documentation + +--- + +## Metrics for Values Adherence + +### Sovereignty & Self-determination +- Zero instances of hidden AI influence +- 100% opt-in for AI features +- User data export capability maintained + +### Transparency & Honesty +- All AI reasoning documented in moderation queue +- Public disclosure of framework limitations +- Clear attribution of AI vs. human content + +### Harmlessness & Protection +- Zero security breaches +- Privacy audit pass rate: 100% +- Fail-safe activation rate (AI defers to human) + +### Human Judgment Primacy +- 100% of values decisions reviewed by humans +- Average escalation response time < 24 hours +- Zero unauthorized AI autonomous actions + +### Community & Accessibility +- WCAG AA compliance: 100% of pages +- Free tier usage: >80% of all users +- Community contributions accepted and integrated + +--- + +## Implementation Requirements + +All features, content, and operations must: + +1. **Pass Values Alignment Check**: Documented review against this framework +2. **Include Tractatus Governance**: Boundary enforcement, classification, validation +3. **Maintain Human Oversight**: Clear escalation paths to human authority +4. **Support Transparency**: Reasoning and decision processes visible +5. **Respect User Sovereignty**: No manipulation, complete control, clear consent + +**Failure to align with these values is grounds for feature rejection or removal.** + +--- + +## Appendix A: Values in Action Examples + +### Example 1: Blog Post Suggestion +**AI Action:** Suggests topic "Is AI Safety Overblown?" +**Classification:** STOCHASTIC (exploration) β†’ escalate to STRATEGIC (values-sensitive) +**Human Review:** Topic involves framework credibility, requires strategic approval +**Decision:** Approved with requirement for balanced, evidence-based treatment +**Outcome:** Blog post published with AI reasoning visible, cites peer-reviewed research + +### Example 2: Media Inquiry Response +**AI Action:** Triages inquiry from major tech publication as "high urgency" +**Classification:** OPERATIONAL (standard process) +**Human Review:** Response drafted by human, reviews AI summary for accuracy +**Decision:** Human-written response sent, AI triage saved time +**Outcome:** Effective media engagement, human authority maintained + +### Example 3: Feature Request +**AI Action:** Suggests adding "auto-approve" for low-risk blog posts +**Classification:** STRATEGIC (changes governance boundary) +**Human Review:** Would reduce human oversight, conflicts with core values +**Decision:** Rejected - all content requires human approval per TRA-VAL-0001 +**Outcome:** Framework integrity preserved, alternative efficiency improvements explored + +--- + +## Appendix B: Glossary + +**AI Governance:** Frameworks and mechanisms that control AI system behavior +**Boundary Enforcement:** Preventing AI from actions outside defined authority +**Dogfooding:** Using the framework to govern itself (meta-implementation) +**Human Judgment Primacy:** Core principle that humans retain decision authority +**Quadrant Classification:** Strategic/Operational/Tactical/System/Stochastic categorization +**Time-Persistence Metadata:** Instruction classification by longevity and importance +**Values-Sensitive:** Content or decisions that intersect with strategic values + +--- + +**Document Authority:** This document has final authority over all platform operations. In case of conflict between this document and any other guidance, TRA-VAL-0001 takes precedence. + +**Next Review:** 2026-10-06 +**Version History:** v1.0 (2025-10-06) - Initial creation + +--- + +*This document is maintained by John Stroh (john.stroh.nz@pm.me) and subject to annual review. Changes require explicit human approval and public documentation.* diff --git a/src/utils/db.util.js b/src/utils/db.util.js new file mode 100644 index 00000000..1cdff2cb --- /dev/null +++ b/src/utils/db.util.js @@ -0,0 +1,111 @@ +/** + * Database Connection Utility + * Manages MongoDB connection with reconnection logic + */ + +const { MongoClient } = require('mongodb'); +const logger = require('./logger.util'); + +class DatabaseConnection { + constructor() { + this.client = null; + this.db = null; + this.uri = process.env.MONGODB_URI || 'mongodb://localhost:27017/tractatus_dev'; + this.dbName = process.env.MONGODB_DB || 'tractatus_dev'; + } + + /** + * Connect to MongoDB with retry logic + */ + async connect(retries = 5) { + if (this.client && this.client.topology && this.client.topology.isConnected()) { + return this.db; + } + + for (let attempt = 1; attempt <= retries; attempt++) { + try { + this.client = new MongoClient(this.uri, { + maxPoolSize: 10, + minPoolSize: 2, + maxIdleTimeMS: 30000, + serverSelectionTimeoutMS: 5000, + socketTimeoutMS: 45000, + }); + + await this.client.connect(); + this.db = this.client.db(this.dbName); + + // Verify connection + await this.db.admin().ping(); + + logger.info(`βœ… Connected to MongoDB: ${this.dbName}`); + return this.db; + + } catch (error) { + logger.error(`MongoDB connection attempt ${attempt}/${retries} failed:`, error.message); + + if (attempt === retries) { + throw new Error(`Failed to connect to MongoDB after ${retries} attempts: ${error.message}`); + } + + // Wait before retry (exponential backoff) + await this.sleep(Math.min(1000 * Math.pow(2, attempt - 1), 10000)); + } + } + } + + /** + * Get database instance (connects if not connected) + */ + async getDb() { + if (!this.db) { + await this.connect(); + } + return this.db; + } + + /** + * Get collection + */ + async getCollection(collectionName) { + const db = await this.getDb(); + return db.collection(collectionName); + } + + /** + * Close connection + */ + async close() { + if (this.client) { + await this.client.close(); + this.client = null; + this.db = null; + logger.info('MongoDB connection closed'); + } + } + + /** + * Check if connected + */ + isConnected() { + return this.client && this.client.topology && this.client.topology.isConnected(); + } + + /** + * Sleep utility for retry logic + */ + sleep(ms) { + return new Promise(resolve => setTimeout(resolve, ms)); + } +} + +// Singleton instance +const dbConnection = new DatabaseConnection(); + +module.exports = { + connect: () => dbConnection.connect(), + getDb: () => dbConnection.getDb(), + getCollection: (name) => dbConnection.getCollection(name), + close: () => dbConnection.close(), + isConnected: () => dbConnection.isConnected() +}; diff --git a/src/utils/jwt.util.js b/src/utils/jwt.util.js new file mode 100644 index 00000000..f014ef58 --- /dev/null +++ b/src/utils/jwt.util.js @@ -0,0 +1,58 @@ +/** + * JWT Utility + * Token generation and verification for admin authentication + */ + +const jwt = require('jsonwebtoken'); + +const JWT_SECRET = process.env.JWT_SECRET || 'CHANGE_THIS_IN_PRODUCTION'; +const JWT_EXPIRY = process.env.JWT_EXPIRY || '7d'; + +/** + * Generate JWT token + */ +function generateToken(payload) { + return jwt.sign(payload, JWT_SECRET, { + expiresIn: JWT_EXPIRY, + issuer: 'tractatus', + audience: 'tractatus-admin' + }); +} + +/** + * Verify JWT token + */ +function verifyToken(token) { + try { + return jwt.verify(token, JWT_SECRET, { + issuer: 'tractatus', + audience: 'tractatus-admin' + }); + } catch (error) { + throw new Error(`Invalid token: ${error.message}`); + } +} + +/** + * Decode token without verification (for debugging) + */ +function decodeToken(token) { + return jwt.decode(token); +} + +/** + * Extract token from Authorization header + */ +function extractTokenFromHeader(authHeader) { + if (!authHeader || !authHeader.startsWith('Bearer ')) { + return null; + } + return authHeader.substring(7); +} + +module.exports = { + generateToken, + verifyToken, + decodeToken, + extractTokenFromHeader +}; diff --git a/src/utils/logger.util.js b/src/utils/logger.util.js new file mode 100644 index 00000000..3eb7e219 --- /dev/null +++ b/src/utils/logger.util.js @@ -0,0 +1,73 @@ +/** + * Logger Utility + * Winston-based logging with file and console output + */ + +const winston = require('winston'); +const path = require('path'); + +const logLevel = process.env.LOG_LEVEL || 'info'; +const logFile = process.env.LOG_FILE || path.join(__dirname, '../../logs/app.log'); + +// Define log format +const logFormat = winston.format.combine( + winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }), + winston.format.errors({ stack: true }), + winston.format.printf(({ timestamp, level, message, stack }) => { + return stack + ? `${timestamp} [${level.toUpperCase()}]: ${message}\n${stack}` + : `${timestamp} [${level.toUpperCase()}]: ${message}`; + }) +); + +// Create logger instance +const logger = winston.createLogger({ + level: logLevel, + format: logFormat, + transports: [ + // Console output (colored in development) + new winston.transports.Console({ + format: winston.format.combine( + winston.format.colorize(), + logFormat + ) + }), + + // File output + new winston.transports.File({ + filename: logFile, + maxsize: 5242880, // 5MB + maxFiles: 5, + }), + + // Error file + new winston.transports.File({ + filename: path.join(path.dirname(logFile), 'error.log'), + level: 'error', + maxsize: 5242880, + maxFiles: 5, + }) + ] +}); + +// Add request logging method +logger.request = (req, res, next) => { + const start = Date.now(); + + res.on('finish', () => { + const duration = Date.now() - start; + const message = `${req.method} ${req.originalUrl} ${res.statusCode} - ${duration}ms`; + + if (res.statusCode >= 500) { + logger.error(message); + } else if (res.statusCode >= 400) { + logger.warn(message); + } else { + logger.info(message); + } + }); + + next(); +}; + +module.exports = logger; diff --git a/src/utils/markdown.util.js b/src/utils/markdown.util.js new file mode 100644 index 00000000..fe439fb7 --- /dev/null +++ b/src/utils/markdown.util.js @@ -0,0 +1,146 @@ +/** + * Markdown Utility + * Convert markdown to HTML with syntax highlighting + */ + +const { marked } = require('marked'); +const hljs = require('highlight.js'); +const sanitizeHtml = require('sanitize-html'); + +// Configure marked +marked.setOptions({ + highlight: function(code, lang) { + if (lang && hljs.getLanguage(lang)) { + try { + return hljs.highlight(code, { language: lang }).value; + } catch (err) { + console.error('Highlight error:', err); + } + } + return hljs.highlightAuto(code).value; + }, + gfm: true, + breaks: false, + pedantic: false, + smartLists: true, + smartypants: true +}); + +/** + * Convert markdown to HTML + */ +function markdownToHtml(markdown) { + if (!markdown) return ''; + + const html = marked(markdown); + + // Sanitize HTML to prevent XSS + return sanitizeHtml(html, { + allowedTags: [ + 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', + 'p', 'br', 'hr', + 'strong', 'em', 'u', 'code', 'pre', + 'a', 'img', + 'ul', 'ol', 'li', + 'blockquote', + 'table', 'thead', 'tbody', 'tr', 'th', 'td', + 'div', 'span', + 'sup', 'sub', + 'del', 'ins' + ], + allowedAttributes: { + 'a': ['href', 'title', 'target', 'rel'], + 'img': ['src', 'alt', 'title', 'width', 'height'], + 'code': ['class'], + 'pre': ['class'], + 'div': ['class'], + 'span': ['class'], + 'table': ['class'], + 'th': ['scope', 'class'], + 'td': ['class'] + }, + allowedClasses: { + 'code': ['language-*', 'hljs', 'hljs-*'], + 'pre': ['hljs'], + 'div': ['highlight'], + 'span': ['hljs-*'] + } + }); +} + +/** + * Extract table of contents from markdown + */ +function extractTOC(markdown) { + if (!markdown) return []; + + const headings = []; + const lines = markdown.split('\n'); + + lines.forEach(line => { + const match = line.match(/^(#{1,6})\s+(.+)$/); + if (match) { + const level = match[1].length; + const title = match[2].replace(/[#*_`]/g, '').trim(); + const slug = title.toLowerCase() + .replace(/[^\w\s-]/g, '') + .replace(/\s+/g, '-') + .replace(/-+/g, '-') + .trim(); + + headings.push({ + level, + title, + slug + }); + } + }); + + return headings; +} + +/** + * Extract front matter from markdown + */ +function extractFrontMatter(markdown) { + if (!markdown) return { metadata: {}, content: markdown }; + + const frontMatterRegex = /^---\s*\n([\s\S]*?)\n---\s*\n([\s\S]*)$/; + const match = markdown.match(frontMatterRegex); + + if (!match) { + return { metadata: {}, content: markdown }; + } + + const frontMatter = match[1]; + const content = match[2]; + const metadata = {}; + + frontMatter.split('\n').forEach(line => { + const [key, ...valueParts] = line.split(':'); + if (key && valueParts.length) { + metadata[key.trim()] = valueParts.join(':').trim(); + } + }); + + return { metadata, content }; +} + +/** + * Generate slug from title + */ +function generateSlug(title) { + return title + .toLowerCase() + .replace(/[^\w\s-]/g, '') + .replace(/\s+/g, '-') + .replace(/-+/g, '-') + .trim(); +} + +module.exports = { + markdownToHtml, + extractTOC, + extractFrontMatter, + generateSlug +};