mirror of
https://github.com/jhd3197/ServerKit.git
synced 2026-10-27 23:28:03 +09:00
Comments, docstrings and a few strings cited plan numbers ("plan 29
#9", "plan 86 §B2") from a git-ignored folder, so they pointed at
nothing; one reached users in a bottleneck hint. Code and frontend
strings are unchanged apart from those citations (checked by AST and
token comparison). A vhost still carrying the old asset comment reads
as undrifted, and a guard test keeps new citations out.
120 lines
4.8 KiB
Python
120 lines
4.8 KiB
Python
"""Installed plugin tracking model."""
|
|
from datetime import datetime
|
|
from app import db
|
|
from app.models.json_column_mixin import JsonColumnMixin
|
|
import json
|
|
|
|
|
|
class InstalledPlugin(JsonColumnMixin, db.Model):
|
|
"""Tracks plugins installed from external sources (zips/URLs)."""
|
|
__tablename__ = 'installed_plugins'
|
|
|
|
id = db.Column(db.Integer, primary_key=True)
|
|
name = db.Column(db.String(128), nullable=False, unique=True)
|
|
display_name = db.Column(db.String(256), nullable=False)
|
|
slug = db.Column(db.String(128), nullable=False, unique=True)
|
|
version = db.Column(db.String(32), nullable=False)
|
|
description = db.Column(db.Text)
|
|
author = db.Column(db.String(128))
|
|
homepage = db.Column(db.String(512))
|
|
repository = db.Column(db.String(512))
|
|
license = db.Column(db.String(64))
|
|
category = db.Column(db.String(64))
|
|
|
|
# Where it came from
|
|
source_url = db.Column(db.String(1024))
|
|
source_type = db.Column(db.String(32), default='url') # url, local, marketplace
|
|
|
|
# Paths relative to ServerKit root
|
|
backend_path = db.Column(db.String(512)) # e.g. app/plugins/serverkit-ai
|
|
frontend_path = db.Column(db.String(512)) # e.g. src/plugins/serverkit-ai
|
|
|
|
# Blueprint registration info from manifest
|
|
entry_point = db.Column(db.String(256)) # e.g. blueprint:ai_assistant_bp
|
|
url_prefix = db.Column(db.String(256)) # e.g. /api/v1/ai-assistant
|
|
|
|
# Frontend entry from manifest
|
|
frontend_entry = db.Column(db.String(256)) # e.g. components/AiAssistant.jsx
|
|
|
|
# Full manifest stored as JSON
|
|
manifest_json = db.Column(db.Text)
|
|
|
|
# Saved per-plugin config values (#49). The manifest's `config_schema`
|
|
# describes the fields; admins edit values from the Marketplace and the
|
|
# plugin reads them via plugins_sdk.config(slug). May hold secrets — kept
|
|
# out of to_dict; served only by the admin-gated config endpoint.
|
|
config_json = db.Column(db.Text)
|
|
|
|
# Status
|
|
STATUS_ACTIVE = 'active'
|
|
STATUS_DISABLED = 'disabled'
|
|
STATUS_ERROR = 'error'
|
|
STATUS_INSTALLING = 'installing'
|
|
status = db.Column(db.String(32), default=STATUS_INSTALLING)
|
|
error_message = db.Column(db.Text)
|
|
|
|
# Has frontend component that needs rebuild
|
|
has_frontend = db.Column(db.Boolean, default=False)
|
|
# Has backend blueprint
|
|
has_backend = db.Column(db.Boolean, default=False)
|
|
|
|
installed_by = db.Column(db.Integer, db.ForeignKey('users.id'), index=True)
|
|
installed_at = db.Column(db.DateTime, default=datetime.utcnow)
|
|
updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
|
|
|
|
@property
|
|
def manifest(self):
|
|
return self._json_read('manifest_json')
|
|
|
|
@manifest.setter
|
|
def manifest(self, v):
|
|
self.manifest_json = json.dumps(v)
|
|
|
|
@property
|
|
def config(self):
|
|
return self._json_read('config_json')
|
|
|
|
@config.setter
|
|
def config(self, v):
|
|
self.config_json = json.dumps(v or {})
|
|
|
|
def to_dict(self):
|
|
manifest = self.manifest or {}
|
|
return {
|
|
'id': self.id,
|
|
'name': self.name,
|
|
'display_name': self.display_name,
|
|
'slug': self.slug,
|
|
'version': self.version,
|
|
'description': self.description,
|
|
'author': self.author,
|
|
'homepage': self.homepage,
|
|
'repository': self.repository,
|
|
'license': self.license,
|
|
'category': self.category,
|
|
'source_url': self.source_url,
|
|
'source_type': self.source_type,
|
|
'entry_point': self.entry_point,
|
|
'url_prefix': self.url_prefix,
|
|
'has_frontend': self.has_frontend,
|
|
'has_backend': self.has_backend,
|
|
'status': self.status,
|
|
'error_message': self.error_message,
|
|
# Surface declarative manifest fields so the install UI can
|
|
# show the user what they're approving without making a
|
|
# second round-trip to fetch the manifest.
|
|
'permissions': manifest.get('permissions') or [],
|
|
'contributions': manifest.get('contributions') or {},
|
|
'templates': manifest.get('templates') or [],
|
|
'lifecycle': manifest.get('lifecycle') or {},
|
|
# Schema only — saved VALUES may hold secrets and are served
|
|
# exclusively by the admin-gated /config endpoint.
|
|
'config_schema': manifest.get('config_schema') or {},
|
|
# Install-time signature verdict: None for sources that
|
|
# predate stamping, else {status: verified|untrusted_key|unsigned,
|
|
# key_id?, publisher?}. Panel-managed (reserved config key).
|
|
'signature': (self.config or {}).get('_signature'),
|
|
'installed_at': self.installed_at.isoformat() if self.installed_at else None,
|
|
'updated_at': self.updated_at.isoformat() if self.updated_at else None,
|
|
}
|