tractatus/src/models/Project.model.js
TheFlow 5557126f6a feat: eliminate all GitHub references from agenticgovernance.digital
- Created /source-code.html — sovereign hosting landing page explaining
  why we left GitHub, how to access the code, and the sovereignty model
- Navbar: GitHub link → Source Code link (desktop + mobile)
- Footer: GitHub link → Source Code link
- Docs sidebar: GitHub section → Source Code section with sovereign repo
- Implementer page: all repository links point to /source-code.html,
  clone instructions updated, CI/CD code example genericised
- FAQ: GitHub Discussions button → Contact Us with email icon
- FAQ content: all 4 locales (en/de/fr/mi) rewritten to remove
  GitHub Actions YAML, GitHub URLs, and GitHub-specific patterns
- faq.js fallback content: same changes as locale files
- agent-lightning integration page: updated to source-code.html
- Project model: example URL changed from GitHub to Codeberg
- All locale files updated: navbar.github → navbar.source_code,
  footer GitHub → source_code, FAQ button text updated in 4 languages

Zero GitHub references remain in any HTML, JS, or JSON file
(only github-dark.min.css theme name in highlight.js CDN reference).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 18:14:26 +13:00

294 lines
6.8 KiB
JavaScript

/**
* Project Model
*
* Stores metadata for projects using the Tractatus governance system.
* Each project can have its own variable values for context-aware rule rendering.
*
* Benefits:
* - Multi-project governance support
* - Context-aware variable substitution
* - Centralized project metadata
* - Tech stack tracking for rule applicability
*/
const mongoose = require('mongoose');
const projectSchema = new mongoose.Schema({
// Project identification
id: {
type: String,
required: true,
unique: true,
index: true,
lowercase: true,
trim: true,
validate: {
validator(v) {
// Slug format: lowercase letters, numbers, hyphens
return /^[a-z0-9-]+$/.test(v);
},
message: 'Project ID must be lowercase alphanumeric with hyphens only (e.g., "tractatus", "family-history")'
},
description: 'Unique project identifier (slug format, e.g., "tractatus", "family-history")'
},
name: {
type: String,
required: true,
trim: true,
minlength: 1,
maxlength: 100,
description: 'Human-readable project name (e.g., "Tractatus Framework")'
},
description: {
type: String,
default: '',
maxlength: 500,
description: 'Brief description of the project and its purpose'
},
// Technology stack information
techStack: {
type: {
language: {
type: String,
default: '',
description: 'Primary programming language (e.g., "JavaScript", "Python")'
},
framework: {
type: String,
default: '',
description: 'Main framework (e.g., "Node.js/Express", "Django", "React")'
},
database: {
type: String,
default: '',
description: 'Database system (e.g., "MongoDB", "PostgreSQL", "MySQL")'
},
frontend: {
type: String,
default: '',
description: 'Frontend technology (e.g., "Vanilla JS", "React", "Vue")'
},
other: {
type: [String],
default: [],
description: 'Other notable technologies or tools'
}
},
default: () => ({
language: '',
framework: '',
database: '',
frontend: '',
other: []
}),
description: 'Technology stack used by this project'
},
// Repository information
repositoryUrl: {
type: String,
default: '',
validate: {
validator(v) {
if (!v) return true; // Empty is valid
// Basic URL validation
try {
new URL(v);
return true;
} catch {
return false;
}
},
message: 'Repository URL must be a valid URL'
},
description: 'Git repository URL (e.g., "https://codeberg.org/org/repo")'
},
// Metadata
metadata: {
type: {
defaultBranch: {
type: String,
default: 'main',
description: 'Default git branch (e.g., "main", "master", "develop")'
},
environment: {
type: String,
enum: ['development', 'staging', 'production', 'test'],
default: 'development',
description: 'Primary environment context for this project instance'
},
lastSynced: {
type: Date,
default: null,
description: 'Last time project data was synced (if applicable)'
},
tags: {
type: [String],
default: [],
description: 'Freeform tags for categorization'
}
},
default: () => ({
defaultBranch: 'main',
environment: 'development',
lastSynced: null,
tags: []
}),
description: 'Additional metadata about the project'
},
// Status
active: {
type: Boolean,
default: true,
index: true,
description: 'Whether this project is currently active'
},
// Audit fields
createdBy: {
type: String,
default: 'system',
description: 'Who created this project'
},
updatedBy: {
type: String,
default: 'system',
description: 'Who last updated this project'
}
}, {
timestamps: true, // Adds createdAt and updatedAt automatically
collection: 'projects'
});
// Indexes for common queries
projectSchema.index({ active: 1, name: 1 });
projectSchema.index({ 'metadata.environment': 1 });
projectSchema.index({ 'techStack.database': 1 });
// Virtual for checking if project has repository
projectSchema.virtual('hasRepository').get(function() {
return !!this.repositoryUrl;
});
// Static methods
/**
* Find all active projects
* @param {Object} options - Query options
* @returns {Promise<Array>}
*/
projectSchema.statics.findActive = function(options = {}) {
const query = { active: true };
return this.find(query)
.sort({ name: 1 })
.limit(options.limit || 0);
};
/**
* Find project by ID (case-insensitive)
* @param {string} projectId - Project ID
* @returns {Promise<Object|null>}
*/
projectSchema.statics.findByProjectId = function(projectId) {
return this.findOne({
id: projectId.toLowerCase(),
active: true
});
};
/**
* Find projects by technology
* @param {string} techType - Type of tech (language/framework/database/frontend)
* @param {string} techValue - Technology value
* @returns {Promise<Array>}
*/
projectSchema.statics.findByTechnology = function(techType, techValue) {
const query = {
active: true,
[`techStack.${techType}`]: new RegExp(techValue, 'i')
};
return this.find(query).sort({ name: 1 });
};
/**
* Get project statistics
* @returns {Promise<Object>}
*/
projectSchema.statics.getStatistics = async function() {
const stats = await this.aggregate([
{
$group: {
_id: null,
totalProjects: { $sum: 1 },
activeProjects: {
$sum: { $cond: ['$active', 1, 0] }
},
inactiveProjects: {
$sum: { $cond: ['$active', 0, 1] }
},
databases: { $addToSet: '$techStack.database' },
languages: { $addToSet: '$techStack.language' }
}
}
]);
return stats[0] || {
totalProjects: 0,
activeProjects: 0,
inactiveProjects: 0,
databases: [],
languages: []
};
};
// Instance methods
/**
* Deactivate project (soft delete)
* @returns {Promise<Object>}
*/
projectSchema.methods.deactivate = async function() {
this.active = false;
this.updatedBy = 'system';
return this.save();
};
/**
* Activate project
* @returns {Promise<Object>}
*/
projectSchema.methods.activate = async function() {
this.active = true;
this.updatedBy = 'system';
return this.save();
};
/**
* Update last synced timestamp
* @returns {Promise<Object>}
*/
projectSchema.methods.updateSyncTimestamp = async function() {
this.metadata.lastSynced = new Date();
return this.save();
};
// Pre-save hook to ensure ID is lowercase
projectSchema.pre('save', function(next) {
if (this.id) {
this.id = this.id.toLowerCase();
}
next();
});
const Project = mongoose.model('Project', projectSchema);
module.exports = Project;