Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions app/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
from app.models.message import Message
from app.models.notification import Notification
from app.models.notification_preference import NotificationPreference
from app.models.plugin import Plugin
from app.models.plugin import PluginInstallation
from app.models.project import Project
from app.models.project_comment import ProjectComment
from app.models.project_file import ProjectFile
Expand All @@ -31,6 +33,8 @@
"Message",
"Notification",
"NotificationPreference",
"Plugin",
"PluginInstallation",
"Project",
"ProjectComment",
"ProjectFile",
Expand Down
108 changes: 108 additions & 0 deletions app/models/plugin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
"""Plugin database models.

Stores plugin metadata, installations, and configuration per workspace.
"""

from datetime import datetime

from app.extensions import db


class Plugin(db.Model):
"""Installed plugin metadata (global)."""

__tablename__ = "plugins"

id = db.Column(db.String(64), primary_key=True) # e.g., "stellar-tools"
name = db.Column(db.String(256), nullable=False)
version = db.Column(db.String(32), nullable=False)
description = db.Column(db.Text)
author = db.Column(db.String(256))
entry_point = db.Column(db.String(512), nullable=False)
capabilities = db.Column(db.JSON, default=[]) # List of capability strings
permissions = db.Column(db.JSON, default=[])
dependencies = db.Column(db.JSON, default=[])
configuration = db.Column(db.JSON, default={})

enabled = db.Column(db.Boolean, default=True)
installed_at = db.Column(db.DateTime, default=datetime.utcnow, nullable=False)
updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow, nullable=False)

# Relationships
installations = db.relationship(
"PluginInstallation",
back_populates="plugin",
cascade="all, delete-orphan",
lazy="select",
)

def to_dict(self) -> dict:
"""Convert to dictionary representation."""
return {
"id": self.id,
"name": self.name,
"version": self.version,
"description": self.description,
"author": self.author,
"entry_point": self.entry_point,
"capabilities": self.capabilities or [],
"permissions": self.permissions or [],
"dependencies": self.dependencies or [],
"configuration": self.configuration or {},
"enabled": self.enabled,
"installed_at": self.installed_at.isoformat() if self.installed_at else None,
"updated_at": self.updated_at.isoformat() if self.updated_at else None,
}

def __repr__(self) -> str:
"""String representation."""
return f"<Plugin {self.id} v{self.version}>"


class PluginInstallation(db.Model):
"""Plugin installation in a specific workspace.

Tracks plugin-specific installation, configuration, and granted capabilities
within a workspace.
"""

__tablename__ = "plugin_installations"

id = db.Column(db.Integer, primary_key=True)
plugin_id = db.Column(db.String(64), db.ForeignKey("plugins.id"), nullable=False, index=True)
workspace_id = db.Column(db.Integer, db.ForeignKey("workspaces.id"), nullable=False, index=True)

enabled = db.Column(db.Boolean, default=True)
granted_capabilities = db.Column(db.JSON, default=[]) # List of capability strings
config = db.Column(db.JSON, default={}) # Workspace-specific plugin configuration

installed_at = db.Column(db.DateTime, default=datetime.utcnow, nullable=False)
updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow, nullable=False)
installed_by_id = db.Column(db.Integer, db.ForeignKey("users.id"), nullable=True)

# Relationships
plugin = db.relationship("Plugin", back_populates="installations", lazy="select")
installed_by = db.relationship("User", foreign_keys=[installed_by_id], lazy="select")
workspace = db.relationship("Workspace", foreign_keys=[workspace_id], lazy="select")

__table_args__ = (
db.UniqueConstraint("plugin_id", "workspace_id", name="uq_plugin_installation"),
)

def to_dict(self) -> dict:
"""Convert to dictionary representation."""
return {
"id": self.id,
"plugin_id": self.plugin_id,
"workspace_id": self.workspace_id,
"enabled": self.enabled,
"granted_capabilities": self.granted_capabilities or [],
"config": self.config or {},
"installed_at": self.installed_at.isoformat() if self.installed_at else None,
"updated_at": self.updated_at.isoformat() if self.updated_at else None,
"installed_by_id": self.installed_by_id,
}

def __repr__(self) -> str:
"""String representation."""
return f"<PluginInstallation plugin={self.plugin_id} workspace={self.workspace_id}>"
Loading
Loading