Add web app support to standalone mode

This commit is contained in:
Tulir Asokan 2021-11-20 00:45:37 +02:00
parent fc516d78b7
commit 8b1685a9ea
4 changed files with 51 additions and 7 deletions

View file

@ -54,7 +54,7 @@ class MaubotServer:
self.runner = web.AppRunner(self.app, access_log_class=AccessLogger) self.runner = web.AppRunner(self.app, access_log_class=AccessLogger)
async def handle_plugin_path(self, request: web.Request) -> web.Response: async def handle_plugin_path(self, request: web.Request) -> web.StreamResponse:
for path, app in self.plugin_routes.items(): for path, app in self.plugin_routes.items():
if request.path.startswith(path): if request.path.startswith(path):
request = request.clone(rel_url=request.rel_url request = request.clone(rel_url=request.rel_url

View file

@ -14,7 +14,6 @@
# You should have received a copy of the GNU Affero General Public License # 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/>. # along with this program. If not, see <https://www.gnu.org/licenses/>.
from typing import Optional, Type, cast from typing import Optional, Type, cast
from aiohttp import ClientSession
import logging.config import logging.config
import importlib import importlib
import argparse import argparse
@ -27,6 +26,8 @@ import sys
from ruamel.yaml import YAML from ruamel.yaml import YAML
from ruamel.yaml.comments import CommentedMap from ruamel.yaml.comments import CommentedMap
import sqlalchemy as sql import sqlalchemy as sql
from aiohttp import web, hdrs, ClientSession
from yarl import URL
from mautrix.util.config import RecursiveDict, BaseMissingError from mautrix.util.config import RecursiveDict, BaseMissingError
from mautrix.util.db import Base from mautrix.util.db import Base
@ -35,7 +36,9 @@ from mautrix.types import (Filter, RoomFilter, RoomEventFilter, StrippedStateEve
EventType, Membership, FilterID, SyncToken) EventType, Membership, FilterID, SyncToken)
from ..plugin_base import Plugin from ..plugin_base import Plugin
from ..plugin_server import PluginWebApp
from ..loader import PluginMeta from ..loader import PluginMeta
from ..server import AccessLogger
from ..matrix import MaubotMatrixClient from ..matrix import MaubotMatrixClient
from ..lib.store_proxy import SyncStoreProxy from ..lib.store_proxy import SyncStoreProxy
from ..__meta__ import __version__ from ..__meta__ import __version__
@ -145,6 +148,25 @@ if meta.config:
log.fatal("Failed to load plugin config", exc_info=True) log.fatal("Failed to load plugin config", exc_info=True)
sys.exit(1) sys.exit(1)
if meta.webapp:
plugin_webapp = PluginWebApp()
web_app = web.Application(router=plugin_webapp)
web_runner = web.AppRunner(web_app, access_log_class=AccessLogger)
web_base_path = config["server.base_path"].rstrip("/")
public_url = str(URL(config["server.public_url"]) / web_base_path).rstrip("/")
# async def _handle_plugin_request(req: web.Request) -> web.StreamResponse:
# assert req.path.startswith(web_base_path)
# req = req.clone(rel_url=req.rel_url
# .with_path(req.rel_url.path[len(web_base_path)])
# .with_query(req.query_string))
# return await plugin_webapp.handle(req)
#
# web_app.router = plugin_webapp
# web_app.router.add_route(hdrs.METH_ANY, web_base_path, _handle_plugin_request)
else:
web_app = web_runner = public_url = plugin_webapp = None
loop = asyncio.get_event_loop() loop = asyncio.get_event_loop()
client: Optional[MaubotMatrixClient] = None client: Optional[MaubotMatrixClient] = None
@ -220,16 +242,25 @@ async def main():
plugin_log = cast(TraceLogger, logging.getLogger("maubot.instance.__main__")) plugin_log = cast(TraceLogger, logging.getLogger("maubot.instance.__main__"))
bot = plugin(client=client, loop=loop, http=http_client, instance_id="__main__", bot = plugin(client=client, loop=loop, http=http_client, instance_id="__main__",
log=plugin_log, config=bot_config, database=db if meta.database else None, log=plugin_log, config=bot_config, database=db if meta.database else None,
webapp=None, webapp_url=None, loader=loader) webapp=plugin_webapp, webapp_url=public_url, loader=loader)
await bot.internal_start() await bot.internal_start()
if web_runner:
await web_runner.setup()
site = web.TCPSite(web_runner, config["server.hostname"], config["server.port"])
await site.start()
log.info(f"Web server listening on {site.name}")
async def stop() -> None: async def stop() -> None:
client.stop() client.stop()
await bot.internal_stop() await bot.internal_stop()
if crypto_db: if crypto_db:
await crypto_db.stop() await crypto_db.stop()
if web_runner:
await web_runner.shutdown()
await web_runner.cleanup()
try: try:

View file

@ -36,12 +36,13 @@ class Config(BaseFileConfig):
copy("user.autojoin") copy("user.autojoin")
copy("user.displayname") copy("user.displayname")
copy("user.avatar_url") copy("user.avatar_url")
if "server" in base:
copy("server.hostname")
copy("server.port")
copy("server.base_path")
copy("server.public_url")
if "database" in base: if "database" in base:
copy("database") copy("database")
if "plugin_config" in base: if "plugin_config" in base:
copy("plugin_config") copy("plugin_config")
if "server" in base:
copy("server.hostname")
copy("server.port")
copy("server.public_url")
copy("logging") copy("logging")

View file

@ -21,6 +21,18 @@ user:
# if you want the bot to handle messages that were sent while the bot was down. # if you want the bot to handle messages that were sent while the bot was down.
ignore_first_sync: true ignore_first_sync: true
# Web server settings. These will only take effect if the plugin requests it using `webapp: true` in the meta file.
server:
# The IP and port to listen to.
hostname: 0.0.0.0
port: 8080
# The base path where the plugin's web resources will be served. Unlike the normal mode,
# the webserver is dedicated for a single bot in standalone mode, so the default path
# is just /. If you want to emulate normal mode, set this to /_matrix/maubot/plugin/something
base_path: /
# The public URL where the resources are available. The base path is automatically appended to this.
public_url: https://example.com
# The database for the plugin. Used for plugin data, the sync token and e2ee data (if enabled). # The database for the plugin. Used for plugin data, the sync token and e2ee data (if enabled).
# SQLite and Postgres are supported. # SQLite and Postgres are supported.
database: sqlite:///bot.db database: sqlite:///bot.db