Let plugins add their endpoints to the main webserver
This commit is contained in:
parent
79c61d6889
commit
f303bd66ab
@ -26,6 +26,8 @@ server:
|
||||
base_path: /_matrix/maubot/v1
|
||||
# The base path for the UI.
|
||||
ui_base_path: /_matrix/maubot
|
||||
# The base path for plugin endpoints. {id} is replaced with the ID of the instance.
|
||||
plugin_base_path: /_matrix/maubot/plugin/{id}
|
||||
# Override path from where to load UI resources.
|
||||
# Set to false to using pkg_resources to find the path.
|
||||
override_resource_path: false
|
||||
|
@ -58,10 +58,10 @@ loop = asyncio.get_event_loop()
|
||||
init_zip_loader(config)
|
||||
db_session = init_db(config)
|
||||
clients = init_client_class(db_session, loop)
|
||||
plugins = init_plugin_instance_class(db_session, config, loop)
|
||||
management_api = init_mgmt_api(config, loop)
|
||||
server = MaubotServer(config, loop)
|
||||
server.app.add_subapp(config["server.base_path"], management_api)
|
||||
plugins = init_plugin_instance_class(db_session, config, server.app, loop)
|
||||
|
||||
for plugin in plugins:
|
||||
plugin.load()
|
||||
|
@ -41,6 +41,7 @@ class Config(BaseFileConfig):
|
||||
copy("server.listen")
|
||||
copy("server.base_path")
|
||||
copy("server.ui_base_path")
|
||||
copy("server.plugin_base_path")
|
||||
copy("server.override_resource_path")
|
||||
copy("server.appservice_base_path")
|
||||
shared_secret = self["server.unshared_secret"]
|
||||
|
@ -13,8 +13,9 @@
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
from typing import Dict, List, Optional
|
||||
from typing import Dict, List, Optional, TYPE_CHECKING
|
||||
from asyncio import AbstractEventLoop
|
||||
from aiohttp import web
|
||||
import os.path
|
||||
import logging
|
||||
import io
|
||||
@ -33,6 +34,9 @@ from .client import Client
|
||||
from .loader import PluginLoader, ZippedPluginLoader
|
||||
from .plugin_base import Plugin
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .server import MaubotServer
|
||||
|
||||
log = logging.getLogger("maubot.instance")
|
||||
|
||||
yaml = YAML()
|
||||
@ -41,6 +45,7 @@ yaml.indent(4)
|
||||
|
||||
class PluginInstance:
|
||||
db: Session = None
|
||||
webserver: 'MaubotServer' = None
|
||||
mb_config: Config = None
|
||||
loop: AbstractEventLoop = None
|
||||
cache: Dict[str, 'PluginInstance'] = {}
|
||||
@ -54,6 +59,7 @@ class PluginInstance:
|
||||
base_cfg: RecursiveDict[CommentedMap]
|
||||
inst_db: sql.engine.Engine
|
||||
inst_db_tables: Dict[str, sql.Table]
|
||||
inst_webapp: web.Application
|
||||
started: bool
|
||||
|
||||
def __init__(self, db_instance: DBPlugin):
|
||||
@ -66,6 +72,7 @@ class PluginInstance:
|
||||
self.plugin = None
|
||||
self.inst_db = None
|
||||
self.inst_db_tables = None
|
||||
self.inst_webapp = None
|
||||
self.base_cfg = None
|
||||
self.cache[self.id] = self
|
||||
|
||||
@ -105,6 +112,8 @@ class PluginInstance:
|
||||
if self.loader.meta.database:
|
||||
db_path = os.path.join(self.mb_config["plugin_directories.db"], self.id)
|
||||
self.inst_db = sql.create_engine(f"sqlite:///{db_path}.db")
|
||||
if self.loader.meta.webapp:
|
||||
self.inst_webapp = self.webserver.get_instance_subapp(self.id)
|
||||
self.log.debug("Plugin instance dependencies loaded")
|
||||
self.loader.references.add(self)
|
||||
self.client.references.add(self)
|
||||
@ -126,6 +135,8 @@ class PluginInstance:
|
||||
ZippedPluginLoader.trash(
|
||||
os.path.join(self.mb_config["plugin_directories.db"], f"{self.id}.db"),
|
||||
reason="deleted")
|
||||
if self.inst_webapp:
|
||||
self.webserver.remove_instance_webapp(self.id)
|
||||
|
||||
def load_config(self) -> CommentedMap:
|
||||
return yaml.load(self.db_instance.config)
|
||||
@ -157,7 +168,7 @@ class PluginInstance:
|
||||
self.config = config_class(self.load_config, lambda: self.base_cfg, self.save_config)
|
||||
self.plugin = cls(client=self.client.client, loop=self.loop, http=self.client.http_client,
|
||||
instance_id=self.id, log=self.log, config=self.config,
|
||||
database=self.inst_db)
|
||||
database=self.inst_db, webapp=self.inst_webapp)
|
||||
try:
|
||||
await self.plugin.start()
|
||||
except Exception:
|
||||
@ -274,8 +285,10 @@ class PluginInstance:
|
||||
# endregion
|
||||
|
||||
|
||||
def init(db: Session, config: Config, loop: AbstractEventLoop) -> List[PluginInstance]:
|
||||
def init(db: Session, config: Config, webserver: 'MaubotServer', loop: AbstractEventLoop) -> List[
|
||||
PluginInstance]:
|
||||
PluginInstance.db = db
|
||||
PluginInstance.mb_config = config
|
||||
PluginInstance.loop = loop
|
||||
PluginInstance.webserver = webserver
|
||||
return PluginInstance.all()
|
||||
|
@ -57,6 +57,7 @@ class PluginMeta(SerializableAttrs['PluginMeta']):
|
||||
|
||||
maubot: Version = Version(__version__)
|
||||
database: bool = False
|
||||
webapp: bool = False
|
||||
license: str = ""
|
||||
extra_files: List[str] = []
|
||||
dependencies: List[str] = []
|
||||
|
@ -17,7 +17,7 @@ from typing import Type, Optional, TYPE_CHECKING
|
||||
from abc import ABC
|
||||
from logging import Logger
|
||||
from asyncio import AbstractEventLoop
|
||||
import functools
|
||||
from aiohttp.web import Application
|
||||
|
||||
from sqlalchemy.engine.base import Engine
|
||||
from aiohttp import ClientSession
|
||||
@ -37,7 +37,7 @@ class Plugin(ABC):
|
||||
|
||||
def __init__(self, client: 'MaubotMatrixClient', loop: AbstractEventLoop, http: ClientSession,
|
||||
instance_id: str, log: Logger, config: Optional['BaseProxyConfig'],
|
||||
database: Optional[Engine]) -> None:
|
||||
database: Optional[Engine], webapp: Optional[Application]) -> None:
|
||||
self.client = client
|
||||
self.loop = loop
|
||||
self.http = http
|
||||
@ -45,6 +45,7 @@ class Plugin(ABC):
|
||||
self.log = log
|
||||
self.config = config
|
||||
self.database = database
|
||||
self.webapp = webapp
|
||||
self._handlers_at_startup = []
|
||||
|
||||
async def start(self) -> None:
|
||||
|
@ -43,11 +43,29 @@ class MaubotServer:
|
||||
|
||||
as_path = PathBuilder(config["server.appservice_base_path"])
|
||||
self.add_route(Method.PUT, as_path.transactions, self.handle_transaction)
|
||||
self.subapps = {}
|
||||
|
||||
self.setup_management_ui()
|
||||
|
||||
self.runner = web.AppRunner(self.app, access_log_class=AccessLogger)
|
||||
|
||||
def get_instance_subapp(self, instance_id: str) -> web.Application:
|
||||
try:
|
||||
return self.subapps[instance_id]
|
||||
except KeyError:
|
||||
app = web.Application(loop=self.loop)
|
||||
self.app.add_subapp(self.config["server.plugin_base_path"].format(id=instance_id), app)
|
||||
self.subapps[instance_id] = app
|
||||
return app
|
||||
|
||||
def remove_instance_webapp(self, instance_id: str) -> None:
|
||||
try:
|
||||
subapp: web.Application = self.subapps.pop(instance_id)
|
||||
except KeyError:
|
||||
return
|
||||
subapp.shutdown()
|
||||
subapp.cleanup()
|
||||
|
||||
def setup_management_ui(self) -> None:
|
||||
ui_base = self.config["server.ui_base_path"]
|
||||
if ui_base == "/":
|
||||
|
Loading…
Reference in New Issue
Block a user