Make maubot http server serve frontend for production

This commit is contained in:
Tulir Asokan 2018-11-11 00:43:58 +02:00
parent 0264f7b794
commit e0306d29b5
7 changed files with 64 additions and 10 deletions

View file

@ -24,6 +24,11 @@ server:
port: 29316 port: 29316
# The base management API path. # The base management API path.
base_path: /_matrix/maubot/v1 base_path: /_matrix/maubot/v1
# The base path for the UI.
ui_base_path: /_matrix/maubot
# Override path from where to load UI resources.
# Set to false to using pkg_resources to find the path.
override_resource_path: false
# The base appservice API path. Use / for legacy appservice API and /_matrix/app/v1 for v1. # The base appservice API path. Use / for legacy appservice API and /_matrix/app/v1 for v1.
appservice_base_path: /_matrix/app/v1 appservice_base_path: /_matrix/app/v1
# The shared secret to sign API access tokens. # The shared secret to sign API access tokens.

View file

@ -26,7 +26,7 @@ from .server import MaubotServer
from .client import Client, init as init_client_class from .client import Client, init as init_client_class
from .loader.zip import init as init_zip_loader from .loader.zip import init as init_zip_loader
from .instance import init as init_plugin_instance_class from .instance import init as init_plugin_instance_class
from .management.api import init as init_management from .management.api import init as init_management_api
from .__meta__ import __version__ from .__meta__ import __version__
parser = argparse.ArgumentParser(description="A plugin-based Matrix bot system.", parser = argparse.ArgumentParser(description="A plugin-based Matrix bot system.",
@ -52,8 +52,9 @@ init_zip_loader(config)
db_session = init_db(config) db_session = init_db(config)
clients = init_client_class(db_session, loop) clients = init_client_class(db_session, loop)
plugins = init_plugin_instance_class(db_session, config, loop) plugins = init_plugin_instance_class(db_session, config, loop)
management_api = init_management(config, loop) management_api = init_management_api(config, loop)
server = MaubotServer(config, management_api, loop) server = MaubotServer(config, loop)
server.app.add_subapp(config["server.base_path"], management_api)
for plugin in plugins: for plugin in plugins:
plugin.load() plugin.load()

View file

@ -38,6 +38,9 @@ class Config(BaseFileConfig):
copy("server.hostname") copy("server.hostname")
copy("server.port") copy("server.port")
copy("server.listen") copy("server.listen")
copy("server.base_path")
copy("server.ui_base_path")
copy("server.override_resource_path")
copy("server.appservice_base_path") copy("server.appservice_base_path")
shared_secret = self["server.unshared_secret"] shared_secret = self["server.unshared_secret"]
if shared_secret is None or shared_secret == "generate": if shared_secret is None or shared_secret == "generate":

View file

@ -15,6 +15,7 @@
# 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 aiohttp import web from aiohttp import web
from ...__meta__ import __version__
from ...config import Config from ...config import Config
routes: web.RouteTableDef = web.RouteTableDef() routes: web.RouteTableDef = web.RouteTableDef()
@ -28,3 +29,10 @@ def set_config(config: Config) -> None:
def get_config() -> Config: def get_config() -> Config:
return _config return _config
@routes.get("/version")
async def version(_: web.Request) -> web.Response:
return web.json_response({
"version": __version__
})

View file

@ -26,6 +26,7 @@
"last 2 ios_saf versions" "last 2 ios_saf versions"
], ],
"proxy": "http://localhost:29316", "proxy": "http://localhost:29316",
"homepage": ".",
"devDependencies": { "devDependencies": {
"sass-lint": "^1.12.1", "sass-lint": "^1.12.1",
"sass-lint-auto-fix": "^0.15.0" "sass-lint-auto-fix": "^0.15.0"

View file

@ -14,7 +14,7 @@
// 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/>.
import React, { Component } from "react" import React, { Component } from "react"
import { BrowserRouter as Router, Switch } from "react-router-dom" import { HashRouter as Router, Switch } from "react-router-dom"
import PrivateRoute from "../components/PrivateRoute" import PrivateRoute from "../components/PrivateRoute"
import Spinner from "../components/Spinner" import Spinner from "../components/Spinner"
import api from "../api" import api from "../api"

View file

@ -18,6 +18,7 @@ import asyncio
from aiohttp import web from aiohttp import web
from aiohttp.abc import AbstractAccessLogger from aiohttp.abc import AbstractAccessLogger
import pkg_resources
from mautrix.api import PathBuilder, Method from mautrix.api import PathBuilder, Method
@ -35,21 +36,56 @@ class AccessLogger(AbstractAccessLogger):
class MaubotServer: class MaubotServer:
log: logging.Logger = logging.getLogger("maubot.server") log: logging.Logger = logging.getLogger("maubot.server")
def __init__(self, config: Config, management: web.Application, def __init__(self, config: Config, loop: asyncio.AbstractEventLoop) -> None:
loop: asyncio.AbstractEventLoop) -> None:
self.loop = loop or asyncio.get_event_loop() self.loop = loop or asyncio.get_event_loop()
self.app = web.Application(loop=self.loop) self.app = web.Application(loop=self.loop)
self.config = config self.config = config
path = PathBuilder(config["server.base_path"])
self.add_route(Method.GET, path.version, self.version)
self.app.add_subapp(config["server.base_path"], management)
as_path = PathBuilder(config["server.appservice_base_path"]) as_path = PathBuilder(config["server.appservice_base_path"])
self.add_route(Method.PUT, as_path.transactions, self.handle_transaction) self.add_route(Method.PUT, as_path.transactions, self.handle_transaction)
self.setup_management_ui()
self.runner = web.AppRunner(self.app, access_log_class=AccessLogger) self.runner = web.AppRunner(self.app, access_log_class=AccessLogger)
def setup_management_ui(self) -> None:
ui_base = self.config["server.ui_base_path"]
if ui_base == "/":
ui_base = ""
directory = (self.config["server.override_resource_path"]
or pkg_resources.resource_filename("maubot", "management/frontend/build"))
self.app.router.add_static(f"{ui_base}/static", f"{directory}/static")
self.setup_static_root_files(directory, ui_base)
with open(f"{directory}/index.html", "r") as file:
index_html = file.read()
@web.middleware
async def frontend_404_middleware(request, handler):
if hasattr(handler, "__self__") and isinstance(handler.__self__, web.StaticResource):
try:
return await handler(request)
except web.HTTPNotFound:
return web.Response(body=index_html, content_type="text/html")
return await handler(request)
self.app.middlewares.append(frontend_404_middleware)
self.app.router.add_get(f"{ui_base}/", lambda _: web.Response(body=index_html,
content_type="text/html"))
self.app.router.add_get(ui_base, lambda _: web.HTTPFound(f"{ui_base}/"))
def setup_static_root_files(self, directory: str, ui_base: str) -> None:
files = {
"asset-manifest.json": "application/json",
"manifest.json": "application/json",
"favicon.png": "image/png",
}
for file, mime in files.items():
with open(f"{directory}/{file}", "rb") as stream:
data = stream.read()
self.app.router.add_get(f"{ui_base}/{file}", lambda _: web.Response(body=data,
content_type=mime))
def add_route(self, method: Method, path: PathBuilder, handler) -> None: def add_route(self, method: Method, path: PathBuilder, handler) -> None:
self.app.router.add_route(method.value, str(path), handler) self.app.router.add_route(method.value, str(path), handler)