diff --git a/public/docs.html b/public/docs.html index 4c589de1..d7affbc4 100644 --- a/public/docs.html +++ b/public/docs.html @@ -534,49 +534,6 @@
Loading...
- -
-

Resources

-
- - - - - -
-
Research Paper (Full)
-
Tractatus Inflection Point Study
-
-
- - - - - -
-
Executive Summary
-
5-minute read, key findings
-
-
- - - - - -
-
Business Case Template
-
Assessment guide for organizations
-
-
-
-
-

diff --git a/public/js/docs-app.js b/public/js/docs-app.js index 391209e9..512f48f6 100644 --- a/public/js/docs-app.js +++ b/public/js/docs-app.js @@ -12,7 +12,7 @@ const CATEGORIES = { 'getting-started': { label: 'šŸ“š Getting Started', icon: 'šŸ“š', - description: 'Introduction, core concepts, and implementation guides', + description: 'Introduction, core concepts, and glossary', order: 1, color: 'blue', bgColor: 'bg-blue-50', @@ -20,22 +20,33 @@ const CATEGORIES = { textColor: 'text-blue-700', collapsed: false }, + 'resources': { + label: 'šŸ“– Resources', + icon: 'šŸ“–', + description: 'Implementation guides and reference materials', + order: 2, + color: 'amber', + bgColor: 'bg-amber-50', + borderColor: 'border-l-4 border-amber-500', + textColor: 'text-amber-700', + collapsed: false + }, 'research-theory': { label: 'šŸ”¬ Research & Theory', icon: 'šŸ”¬', description: 'Research papers, case studies, theoretical foundations', - order: 2, + order: 3, color: 'purple', bgColor: 'bg-purple-50', borderColor: 'border-l-4 border-purple-500', textColor: 'text-purple-700', - collapsed: false // Expanded to show Working Paper v0.1 + collapsed: true }, 'technical-reference': { label: 'šŸ”Œ Technical Reference', icon: 'šŸ”Œ', description: 'API documentation, code examples, architecture', - order: 3, + order: 4, color: 'green', bgColor: 'bg-green-50', borderColor: 'border-l-4 border-green-500', @@ -46,7 +57,7 @@ const CATEGORIES = { label: 'šŸŽ“ Advanced Topics', icon: 'šŸŽ“', description: 'Value pluralism, organizational theory, advanced concepts', - order: 4, + order: 5, color: 'teal', bgColor: 'bg-teal-50', borderColor: 'border-l-4 border-teal-500', @@ -57,7 +68,7 @@ const CATEGORIES = { label: 'šŸ’¼ Business & Leadership', icon: 'šŸ’¼', description: 'Business cases, ROI analysis, executive briefs', - order: 5, + order: 6, color: 'pink', bgColor: 'bg-pink-50', borderColor: 'border-l-4 border-pink-500', @@ -172,13 +183,8 @@ async function loadDocuments() { const data = await response.json(); documents = data.documents || []; - // Fetch archived documents - const archivedResponse = await fetch('/api/documents/archived'); - const archivedData = await archivedResponse.json(); - const archivedDocuments = archivedData.documents || []; - const listEl = document.getElementById('document-list'); - if (documents.length === 0 && archivedDocuments.length === 0) { + if (documents.length === 0) { listEl.innerHTML = '
No documents available
'; return; } @@ -228,40 +234,6 @@ async function loadDocuments() { `; }); - // Add Archives section if there are archived documents - if (archivedDocuments.length > 0) { - html += ` -
- -
- `; - - // Render archived documents - archivedDocuments.forEach(doc => { - html += renderDocLink(doc, false); - // Add archive note if available - if (doc.archiveNote) { - html += `
${doc.archiveNote}
`; - } - }); - - html += ` -
-
- `; - } - listEl.innerHTML = html; // Apply collapsed state to categories (CSP-compliant - no inline styles) diff --git a/public/service-worker.js b/public/service-worker.js index 4022c23c..0dfea602 100644 --- a/public/service-worker.js +++ b/public/service-worker.js @@ -5,7 +5,7 @@ * - PWA functionality */ -const CACHE_VERSION = '0.1.1'; +const CACHE_VERSION = '0.1.4'; const CACHE_NAME = `tractatus-v${CACHE_VERSION}`; const VERSION_CHECK_INTERVAL = 3600000; // 1 hour in milliseconds diff --git a/scripts/check-glossary.js b/scripts/check-glossary.js new file mode 100644 index 00000000..a90f7419 --- /dev/null +++ b/scripts/check-glossary.js @@ -0,0 +1,48 @@ +/** + * Check Glossary documents in production + */ +const { MongoClient } = require('mongodb'); +require('dotenv').config({ path: '/var/www/tractatus/.env' }); + +async function run() { + const MONGODB_URI = process.env.MONGODB_URI || 'mongodb://localhost:27017'; + const DB_NAME = process.env.MONGODB_DB || 'tractatus_prod'; + + const client = new MongoClient(MONGODB_URI); + await client.connect(); + + console.log('═══════════════════════════════════════════════════════════'); + console.log(' GLOSSARY DOCUMENTS IN PRODUCTION'); + console.log('═══════════════════════════════════════════════════════════\n'); + + const docs = await client.db(DB_NAME).collection('documents').find({ + slug: { $regex: 'glossary', $options: 'i' } + }).project({ + slug: 1, + title: 1, + visibility: 1, + category: 1, + order: 1, + updated_at: 1 + }).toArray(); + + if (docs.length === 0) { + console.log('āŒ No glossary documents found\n'); + } else { + docs.forEach((doc, idx) => { + console.log(`${idx + 1}. ${doc.title}`); + console.log(` Slug: ${doc.slug}`); + console.log(` Visibility: ${doc.visibility}`); + console.log(` Category: ${doc.category || 'none'}`); + console.log(` Order: ${doc.order || 'none'}`); + console.log(` Updated: ${doc.updated_at || 'unknown'}`); + console.log(''); + }); + } + + await client.close(); + + console.log('═══════════════════════════════════════════════════════════\n'); +} + +run().catch(console.error); diff --git a/scripts/compare-glossaries.js b/scripts/compare-glossaries.js new file mode 100644 index 00000000..7c28cffe --- /dev/null +++ b/scripts/compare-glossaries.js @@ -0,0 +1,97 @@ +/** + * Compare Glossary documents in dev and production + */ +const { MongoClient } = require('mongodb'); +require('dotenv').config({ path: '/var/www/tractatus/.env' }); + +async function run() { + console.log('═══════════════════════════════════════════════════════════'); + console.log(' COMPARING GLOSSARY DOCUMENTS'); + console.log('═══════════════════════════════════════════════════════════\n'); + + // Check dev + const devClient = new MongoClient('mongodb://localhost:27017'); + await devClient.connect(); + + const devDocs = await devClient.db('tractatus_dev').collection('documents').find({ + slug: { $regex: 'glossary', $options: 'i' } + }).project({ + slug: 1, + title: 1, + visibility: 1, + category: 1, + sections: 1 + }).toArray(); + + await devClient.close(); + + console.log(`šŸ“š DEV Database: ${devDocs.length} glossary document(s)\n`); + + devDocs.forEach((doc, idx) => { + console.log(`${idx + 1}. ${doc.title}`); + console.log(` Slug: ${doc.slug}`); + console.log(` Visibility: ${doc.visibility}`); + console.log(` Category: ${doc.category || 'none'}`); + console.log(` Sections: ${doc.sections?.length || 0}`); + console.log(''); + }); + + // Check production + const MONGODB_URI = process.env.MONGODB_URI || 'mongodb://localhost:27017'; + const DB_NAME = process.env.MONGODB_DB || 'tractatus_prod'; + + const prodClient = new MongoClient(MONGODB_URI); + await prodClient.connect(); + + const prodDocs = await prodClient.db(DB_NAME).collection('documents').find({ + slug: { $regex: 'glossary', $options: 'i' } + }).project({ + slug: 1, + title: 1, + visibility: 1, + category: 1, + sections: 1 + }).toArray(); + + await prodClient.close(); + + console.log(`šŸ“¦ PRODUCTION Database: ${prodDocs.length} glossary document(s)\n`); + + prodDocs.forEach((doc, idx) => { + console.log(`${idx + 1}. ${doc.title}`); + console.log(` Slug: ${doc.slug}`); + console.log(` Visibility: ${doc.visibility}`); + console.log(` Category: ${doc.category || 'none'}`); + console.log(` Sections: ${doc.sections?.length || 0}`); + console.log(''); + }); + + console.log('═══════════════════════════════════════════════════════════'); + console.log(' RECOMMENDATION'); + console.log('═══════════════════════════════════════════════════════════\n'); + + // Find the best one + const allDocs = [...devDocs.map(d => ({...d, source: 'dev'})), ...prodDocs.map(d => ({...d, source: 'prod'}))]; + const best = allDocs.reduce((best, doc) => { + const sectionsCount = doc.sections?.length || 0; + const bestSections = best.sections?.length || 0; + return sectionsCount > bestSections ? doc : best; + }, allDocs[0]); + + if (best) { + console.log(`Best version: ${best.slug} (${best.source})`); + console.log(` ${best.sections?.length || 0} sections`); + console.log(` Currently: ${best.visibility}`); + console.log(''); + + if (best.visibility !== 'public') { + console.log('āœ… Action: Unarchive this document and set visibility=public'); + } else { + console.log('āœ… Already public - no action needed'); + } + } + + console.log(''); +} + +run().catch(console.error); diff --git a/scripts/diagnose-research-migration.js b/scripts/diagnose-research-migration.js new file mode 100644 index 00000000..a913d439 --- /dev/null +++ b/scripts/diagnose-research-migration.js @@ -0,0 +1,80 @@ +/** + * Diagnose research document migration issue + */ +const { MongoClient } = require('mongodb'); +require('dotenv').config({ path: '/var/www/tractatus/.env' }); + +async function run() { + console.log('═══════════════════════════════════════════════════════════'); + console.log(' DIAGNOSING RESEARCH DOCUMENT MIGRATION'); + console.log('═══════════════════════════════════════════════════════════\n'); + + // 1. Check dev database + console.log('1ļøāƒ£ Checking DEV database...\n'); + const devClient = new MongoClient('mongodb://localhost:27017'); + await devClient.connect(); + const devDoc = await devClient.db('tractatus_dev').collection('documents').findOne({ + slug: 'tractatus-framework-research' + }); + + if (devDoc) { + console.log(` āœ… Found in dev: ${devDoc.title}`); + console.log(` Category: ${devDoc.category}`); + console.log(` Order: ${devDoc.order}`); + console.log(` Sections: ${devDoc.sections?.length || 0}`); + console.log(` Visibility: ${devDoc.visibility}`); + } else { + console.log(' āŒ NOT found in dev'); + } + await devClient.close(); + + // 2. Check production database + console.log('\n2ļøāƒ£ Checking PRODUCTION database...\n'); + + const MONGODB_URI = process.env.MONGODB_URI; + const DB_NAME = process.env.MONGODB_DB || 'tractatus_prod'; + + console.log(` URI: ${MONGODB_URI?.replace(/:[^:]*@/, ':***@')}`); + console.log(` Database: ${DB_NAME}\n`); + + const prodClient = new MongoClient(MONGODB_URI); + await prodClient.connect(); + + const prodDoc = await prodClient.db(DB_NAME).collection('documents').findOne({ + slug: 'tractatus-framework-research' + }); + + if (prodDoc) { + console.log(` āœ… Found in production: ${prodDoc.title}`); + console.log(` Category: ${prodDoc.category}`); + console.log(` Order: ${prodDoc.order}`); + console.log(` Sections: ${prodDoc.sections?.length || 0}`); + console.log(` Visibility: ${prodDoc.visibility}`); + } else { + console.log(' āŒ NOT found in production'); + } + + // 3. Check all research-theory documents in production + console.log('\n3ļøāƒ£ All research-theory documents in production:\n'); + + const researchDocs = await prodClient.db(DB_NAME).collection('documents') + .find({ category: 'research-theory' }) + .project({ slug: 1, title: 1, order: 1 }) + .sort({ order: 1 }) + .toArray(); + + researchDocs.forEach(doc => { + console.log(` [${doc.order}] ${doc.slug}`); + console.log(` ${doc.title}`); + }); + + console.log(`\n Total: ${researchDocs.length} documents in research-theory`); + + await prodClient.close(); + + console.log('\n═══════════════════════════════════════════════════════════'); + console.log(' DIAGNOSIS COMPLETE'); + console.log('═══════════════════════════════════════════════════════════\n'); +} + +run().catch(console.error); diff --git a/scripts/export-research-and-import.js b/scripts/export-research-and-import.js new file mode 100644 index 00000000..7223db3d --- /dev/null +++ b/scripts/export-research-and-import.js @@ -0,0 +1,91 @@ +/** + * Export tractatus-framework-research from dev and import to production + */ +const { MongoClient } = require('mongodb'); +require('dotenv').config({ path: '/var/www/tractatus/.env' }); + +async function run() { + // 1. Export from dev + const devClient = new MongoClient('mongodb://localhost:27017'); + await devClient.connect(); + const devDoc = await devClient.db('tractatus_dev').collection('documents').findOne({ + slug: 'tractatus-framework-research' + }); + await devClient.close(); + + if (!devDoc) { + console.log('āŒ Document not found in dev'); + process.exit(1); + } + + console.log(`āœ… Exported from dev: ${devDoc.title}`); + console.log(` Sections: ${devDoc.sections?.length}`); + + // 2. Prep for production + delete devDoc._id; + devDoc.category = 'research-theory'; + devDoc.order = 2; + devDoc.visibility = 'public'; + devDoc.updated_at = new Date(); + + // 3. Import to production + const MONGODB_URI = process.env.MONGODB_URI || 'mongodb://localhost:27017'; + const DB_NAME = process.env.MONGODB_DB || 'tractatus_prod'; + + const prodClient = new MongoClient(MONGODB_URI); + await prodClient.connect(); + + const existing = await prodClient.db(DB_NAME).collection('documents').findOne({ + slug: 'tractatus-framework-research' + }); + + if (existing) { + console.log('āš ļø Already exists - replacing...'); + await prodClient.db(DB_NAME).collection('documents').replaceOne( + { slug: 'tractatus-framework-research' }, + devDoc + ); + } else { + console.log('šŸ“ Inserting new...'); + await prodClient.db(DB_NAME).collection('documents').insertOne(devDoc); + } + + await prodClient.close(); + + console.log('āœ… Imported to production'); + + // 4. Now run the organization fix on dev too + const devClient2 = new MongoClient('mongodb://localhost:27017'); + await devClient2.connect(); + + console.log('\nšŸ“ Updating dev database...'); + + await devClient2.db('tractatus_dev').collection('documents').updateOne( + { slug: 'executive-summary-tractatus-inflection-point' }, + { + $set: { + category: 'research-theory', + order: 1, + updated_at: new Date() + } + } + ); + + await devClient2.db('tractatus_dev').collection('documents').updateOne( + { slug: 'tractatus-framework-research' }, + { + $set: { + category: 'research-theory', + order: 2, + updated_at: new Date() + } + } + ); + + console.log('āœ… Dev database updated'); + await devClient2.close(); + + console.log('\nāœ… COMPLETE - Both documents now in research-theory'); +} + +run().catch(console.error); diff --git a/scripts/export-research-doc.js b/scripts/export-research-doc.js new file mode 100644 index 00000000..d7bb29b8 --- /dev/null +++ b/scripts/export-research-doc.js @@ -0,0 +1,40 @@ +/** + * Export tractatus-framework-research document to JSON file + */ +const { MongoClient } = require('mongodb'); +const fs = require('fs'); + +async function run() { + const devClient = new MongoClient('mongodb://localhost:27017'); + await devClient.connect(); + + const devDoc = await devClient.db('tractatus_dev').collection('documents').findOne({ + slug: 'tractatus-framework-research' + }); + + await devClient.close(); + + if (!devDoc) { + console.log('āŒ Document not found in dev'); + process.exit(1); + } + + // Prep for production + delete devDoc._id; + devDoc.category = 'research-theory'; + devDoc.order = 2; + devDoc.visibility = 'public'; + devDoc.updated_at = new Date(); + + // Write to file + fs.writeFileSync( + '/tmp/tractatus-framework-research.json', + JSON.stringify(devDoc, null, 2) + ); + + console.log(`āœ… Exported: ${devDoc.title}`); + console.log(` Sections: ${devDoc.sections?.length || 0}`); + console.log(` File: /tmp/tractatus-framework-research.json`); +} + +run().catch(console.error); diff --git a/scripts/fix-document-organization.js b/scripts/fix-document-organization.js new file mode 100644 index 00000000..476b307b --- /dev/null +++ b/scripts/fix-document-organization.js @@ -0,0 +1,108 @@ +/** + * Fix document organization issues: + * 1. Move Executive Brief to research-theory + * 2. Migrate tractatus-framework-research to production + * 3. Ensure both are viewable as card documents + */ +const { MongoClient } = require('mongodb'); +require('dotenv').config({ path: '/var/www/tractatus/.env' }); + +const MONGODB_URI = process.env.MONGODB_URI || 'mongodb://localhost:27017'; +const DB_NAME = process.env.MONGODB_DB || 'tractatus_prod'; + +async function run() { + const client = new MongoClient(MONGODB_URI); + await client.connect(); + + const db = client.db(DB_NAME); + const collection = db.collection('documents'); + + console.log('═══════════════════════════════════════════════════════════'); + console.log(' FIXING DOCUMENT ORGANIZATION'); + console.log('═══════════════════════════════════════════════════════════\n'); + + // 1. Move Executive Brief from getting-started to research-theory + console.log('1ļøāƒ£ Moving Executive Brief to Research & Theory...'); + + const execBriefResult = await collection.updateOne( + { slug: 'executive-summary-tractatus-inflection-point' }, + { + $set: { + category: 'research-theory', + order: 1, // First in research section + updated_at: new Date() + } + } + ); + + if (execBriefResult.modifiedCount > 0) { + console.log(' āœ… Executive Brief moved to research-theory (order: 1)'); + } else { + console.log(' āš ļø Executive Brief not found or already correct'); + } + + // 2. Check if tractatus-framework-research exists in production + console.log('\n2ļøāƒ£ Checking for tractatus-framework-research...'); + + const researchDoc = await collection.findOne({ slug: 'tractatus-framework-research' }); + + if (!researchDoc) { + console.log(' āŒ Document missing from production - needs migration from dev'); + console.log(' šŸ“ Run migration script to copy from dev to prod'); + } else { + console.log(' āœ… Document exists in production'); + console.log(` Category: ${researchDoc.category}`); + console.log(` Order: ${researchDoc.order}`); + console.log(` Sections: ${researchDoc.sections?.length || 0}`); + + // Ensure it's in research-theory with correct order + await collection.updateOne( + { slug: 'tractatus-framework-research' }, + { + $set: { + category: 'research-theory', + order: 2, // Second in research section (after Executive Brief) + updated_at: new Date() + } + } + ); + console.log(' āœ… Updated category and order'); + } + + // 3. Renumber other research-theory documents + console.log('\n3ļøāƒ£ Renumbering research-theory documents...'); + + const researchDocs = await collection.find({ category: 'research-theory' }).toArray(); + console.log(` Found ${researchDocs.length} research documents`); + + const orderedSlugs = [ + 'executive-summary-tractatus-inflection-point', // 1 + 'tractatus-framework-research', // 2 + 'pluralistic-values-research-foundations', // 3 + 'the-27027-incident-a-case-study-in-pattern-recognition-bias', // 4 + 'real-world-ai-governance-a-case-study-in-framework-failure-and-recovery', // 5 + 'llm-integration-feasibility-research-scope', // 6 + 'research-topic-concurrent-session-architecture', // 7 + 'research-topic-rule-proliferation-transactional-overhead' // 8 + ]; + + for (let i = 0; i < orderedSlugs.length; i++) { + await collection.updateOne( + { slug: orderedSlugs[i] }, + { $set: { order: i + 1, updated_at: new Date() } } + ); + } + + console.log(` āœ… Updated order for ${orderedSlugs.length} documents`); + + console.log('\n═══════════════════════════════════════════════════════════'); + console.log(' SUMMARY'); + console.log('═══════════════════════════════════════════════════════════\n'); + console.log('āœ… Executive Brief moved to research-theory'); + console.log('āœ… Document ordering updated'); + console.log(''); + + await client.close(); +} + +run().catch(console.error); diff --git a/scripts/import-research-doc.js b/scripts/import-research-doc.js new file mode 100644 index 00000000..e7ce9f55 --- /dev/null +++ b/scripts/import-research-doc.js @@ -0,0 +1,71 @@ +/** + * Import tractatus-framework-research document from JSON file + */ +const { MongoClient } = require('mongodb'); +const fs = require('fs'); +require('dotenv').config({ path: '/var/www/tractatus/.env' }); + +async function run() { + console.log('═══════════════════════════════════════════════════════════'); + console.log(' IMPORTING RESEARCH DOCUMENT TO PRODUCTION'); + console.log('═══════════════════════════════════════════════════════════\n'); + + // Read JSON file + const doc = JSON.parse(fs.readFileSync('/tmp/tractatus-framework-research.json', 'utf8')); + + console.log(`šŸ“„ Loaded: ${doc.title}`); + console.log(` Slug: ${doc.slug}`); + console.log(` Category: ${doc.category}`); + console.log(` Order: ${doc.order}`); + console.log(` Sections: ${doc.sections?.length || 0}\n`); + + // Connect to production + const MONGODB_URI = process.env.MONGODB_URI || 'mongodb://localhost:27017'; + const DB_NAME = process.env.MONGODB_DB || 'tractatus_prod'; + + console.log(`Database: ${DB_NAME}`); + console.log(`URI: ${MONGODB_URI.replace(/:[^:]*@/, ':***@')}\n`); + + const client = new MongoClient(MONGODB_URI); + await client.connect(); + + const db = client.db(DB_NAME); + const collection = db.collection('documents'); + + // Check if exists + const existing = await collection.findOne({ slug: doc.slug }); + + if (existing) { + console.log('āš ļø Document already exists - replacing...\n'); + + const result = await collection.replaceOne( + { slug: doc.slug }, + doc + ); + + console.log(`āœ… Replaced: ${result.modifiedCount} document(s)`); + } else { + console.log('šŸ“ Inserting new document...\n'); + + const result = await collection.insertOne(doc); + + console.log(`āœ… Inserted with ID: ${result.insertedId}`); + } + + // Verify + const verification = await collection.findOne( + { slug: doc.slug }, + { projection: { title: 1, category: 1, order: 1, visibility: 1 } } + ); + + console.log('\nšŸ” Verification:'); + console.log(JSON.stringify(verification, null, 2)); + + await client.close(); + + console.log('\n═══════════════════════════════════════════════════════════'); + console.log(' IMPORT COMPLETE'); + console.log('═══════════════════════════════════════════════════════════\n'); +} + +run().catch(console.error); diff --git a/scripts/migrate-research-doc-to-prod.js b/scripts/migrate-research-doc-to-prod.js new file mode 100644 index 00000000..7b6187fd --- /dev/null +++ b/scripts/migrate-research-doc-to-prod.js @@ -0,0 +1,75 @@ +/** + * Migrate tractatus-framework-research from dev to production + */ +const { MongoClient } = require('mongodb'); +require('dotenv').config({ path: '/var/www/tractatus/.env' }); + +const MONGODB_URI = process.env.MONGODB_URI || 'mongodb://localhost:27017'; + +async function run() { + // Connect to dev + const devClient = new MongoClient('mongodb://localhost:27017'); + await devClient.connect(); + const devDb = devClient.db('tractatus_dev'); + + // Connect to prod + const prodClient = new MongoClient(MONGODB_URI); + await prodClient.connect(); + const prodDb = prodClient.db(process.env.MONGODB_DB || 'tractatus_prod'); + + console.log('═══════════════════════════════════════════════════════════'); + console.log(' MIGRATING tractatus-framework-research TO PRODUCTION'); + console.log('═══════════════════════════════════════════════════════════\n'); + + // Get document from dev + const devDoc = await devDb.collection('documents').findOne({ slug: 'tractatus-framework-research' }); + + if (!devDoc) { + console.log('āŒ Document not found in dev database'); + await devClient.close(); + await prodClient.close(); + process.exit(1); + } + + console.log(`šŸ“„ Found in dev: ${devDoc.title}`); + console.log(` Sections: ${devDoc.sections?.length || 0}`); + console.log(` Category: ${devDoc.category}`); + + // Check if already exists in prod + const prodDoc = await prodDb.collection('documents').findOne({ slug: 'tractatus-framework-research' }); + + if (prodDoc) { + console.log('āš ļø Document already exists in production - updating instead...'); + + await prodDb.collection('documents').replaceOne( + { slug: 'tractatus-framework-research' }, + devDoc + ); + + console.log('āœ… Document updated in production'); + } else { + console.log('šŸ“ Inserting new document into production...'); + + // Remove _id to let MongoDB generate a new one + delete devDoc._id; + + // Ensure correct metadata + devDoc.category = 'research-theory'; + devDoc.order = 2; + devDoc.visibility = 'public'; + devDoc.updated_at = new Date(); + + await prodDb.collection('documents').insertOne(devDoc); + + console.log('āœ… Document inserted into production'); + } + + await devClient.close(); + await prodClient.close(); + + console.log('\n═══════════════════════════════════════════════════════════'); + console.log(' MIGRATION COMPLETE'); + console.log('═══════════════════════════════════════════════════════════\n'); +} + +run().catch(console.error); diff --git a/scripts/reorganize-document-categories.js b/scripts/reorganize-document-categories.js new file mode 100644 index 00000000..79bb49d3 --- /dev/null +++ b/scripts/reorganize-document-categories.js @@ -0,0 +1,102 @@ +/** + * Reorganize document categories as requested: + * 1. Move implementation guides from getting-started to resources category + * 2. Move Glossary from technical-reference to getting-started + * 3. Ensure Research & Theory section is collapsed by default + */ +const { MongoClient } = require('mongodb'); +require('dotenv').config({ path: '/var/www/tractatus/.env' }); + +async function run() { + const MONGODB_URI = process.env.MONGODB_URI || 'mongodb://localhost:27017'; + const DB_NAME = process.env.MONGODB_DB || 'tractatus_prod'; + + const client = new MongoClient(MONGODB_URI); + await client.connect(); + + console.log('═══════════════════════════════════════════════════════════'); + console.log(' REORGANIZING DOCUMENT CATEGORIES'); + console.log('═══════════════════════════════════════════════════════════\n'); + + const db = client.db(DB_NAME); + const collection = db.collection('documents'); + + // 1. Find guides in Getting Started + console.log('1ļøāƒ£ Finding implementation guides in Getting Started...\n'); + + const guides = await collection.find({ + category: 'getting-started', + slug: { $in: [ + 'implementation-guide-v1.1', + 'implementation-guide', + 'implementation-guide-python-examples' + ]} + }).project({ slug: 1, title: 1 }).toArray(); + + console.log(`Found ${guides.length} guides:`); + guides.forEach(g => console.log(` - ${g.slug}`)); + + // 2. Move guides to resources category + if (guides.length > 0) { + console.log('\nšŸ“ Moving guides to resources category...\n'); + + const result = await collection.updateMany( + { slug: { $in: guides.map(g => g.slug) } }, + { + $set: { + category: 'resources', + updated_at: new Date() + } + } + ); + + console.log(`āœ… Moved ${result.modifiedCount} guides to resources`); + } + + // 3. Move Glossary to getting-started + console.log('\n2ļøāƒ£ Moving Glossary to Getting Started...\n'); + + const glossaryResult = await collection.updateOne( + { slug: 'GLOSSARY' }, + { + $set: { + category: 'getting-started', + order: 10, + updated_at: new Date() + } + } + ); + + if (glossaryResult.modifiedCount > 0) { + console.log('āœ… Glossary moved to getting-started (order: 10)'); + } else { + console.log('āš ļø Glossary not found or already in getting-started'); + } + + // 4. Verify final state + console.log('\n3ļøāƒ£ Verifying final categories...\n'); + + const gettingStarted = await collection.find({ category: 'getting-started', visibility: 'public' }) + .project({ slug: 1, title: 1, order: 1 }) + .sort({ order: 1 }) + .toArray(); + + console.log(`Getting Started (${gettingStarted.length} documents):`); + gettingStarted.forEach(d => console.log(` [${d.order}] ${d.slug}`)); + + const resources = await collection.find({ category: 'resources', visibility: 'public' }) + .project({ slug: 1, title: 1, order: 1 }) + .sort({ order: 1 }) + .toArray(); + + console.log(`\nResources (${resources.length} documents):`); + resources.forEach(d => console.log(` [${d.order || 'none'}] ${d.slug}`)); + + await client.close(); + + console.log('\n═══════════════════════════════════════════════════════════'); + console.log(' REORGANIZATION COMPLETE'); + console.log('═══════════════════════════════════════════════════════════\n'); +} + +run().catch(console.error); diff --git a/scripts/unarchive-glossary.js b/scripts/unarchive-glossary.js new file mode 100644 index 00000000..d8a6ff12 --- /dev/null +++ b/scripts/unarchive-glossary.js @@ -0,0 +1,92 @@ +/** + * Unarchive and publish the best Glossary document + */ +const { MongoClient } = require('mongodb'); +require('dotenv').config({ path: '/var/www/tractatus/.env' }); + +async function run() { + const MONGODB_URI = process.env.MONGODB_URI || 'mongodb://localhost:27017'; + const DB_NAME = process.env.MONGODB_DB || 'tractatus_prod'; + + const client = new MongoClient(MONGODB_URI); + await client.connect(); + + console.log('═══════════════════════════════════════════════════════════'); + console.log(' UNARCHIVING GLOSSARY'); + console.log('═══════════════════════════════════════════════════════════\n'); + + const db = client.db(DB_NAME); + const collection = db.collection('documents'); + + // Find all glossary documents + const glossaries = await collection.find({ + slug: { $regex: 'glossary', $options: 'i' } + }).toArray(); + + console.log(`Found ${glossaries.length} glossary document(s)\n`); + + // Pick the one with most sections + let best = null; + let bestSections = 0; + + glossaries.forEach((doc, idx) => { + const sections = doc.sections?.length || 0; + console.log(`${idx + 1}. ${doc.slug}`); + console.log(` Title: ${doc.title}`); + console.log(` Sections: ${sections}`); + console.log(` Visibility: ${doc.visibility}`); + console.log(` Category: ${doc.category || 'none'}`); + console.log(''); + + if (sections > bestSections) { + best = doc; + bestSections = sections; + } + }); + + if (!best) { + console.log('āŒ No glossary documents found'); + await client.close(); + return; + } + + console.log(`āœ… Best version: ${best.slug} (${bestSections} sections)\n`); + + // Unarchive it + if (best.visibility !== 'public') { + console.log('šŸ“ Updating to public...\n'); + + const result = await collection.updateOne( + { _id: best._id }, + { + $set: { + visibility: 'public', + category: 'technical-reference', + order: 100, // Place at end of technical reference + updated_at: new Date() + } + } + ); + + console.log(`āœ… Updated: ${result.modifiedCount} document(s)`); + + // Verify + const updated = await collection.findOne( + { _id: best._id }, + { projection: { slug: 1, title: 1, visibility: 1, category: 1, order: 1 } } + ); + + console.log('\nšŸ” Verification:'); + console.log(JSON.stringify(updated, null, 2)); + } else { + console.log('āœ… Already public - no update needed'); + } + + await client.close(); + + console.log('\n═══════════════════════════════════════════════════════════'); + console.log(' COMPLETE'); + console.log('═══════════════════════════════════════════════════════════\n'); +} + +run().catch(console.error); diff --git a/src/routes/documents.routes.js b/src/routes/documents.routes.js index 1ef49c19..856f585b 100644 --- a/src/routes/documents.routes.js +++ b/src/routes/documents.routes.js @@ -20,8 +20,10 @@ router.get('/search', asyncHandler(documentsController.searchDocuments) ); -// GET /api/documents/archived +// GET /api/documents/archived (admin only) router.get('/archived', + authenticateToken, + requireRole('admin'), asyncHandler(documentsController.listArchivedDocuments) );