Make maubot http server serve frontend for production
This commit is contained in:
parent
0264f7b794
commit
e0306d29b5
7 changed files with 64 additions and 10 deletions
|
@ -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.
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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":
|
||||||
|
|
|
@ -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__
|
||||||
|
})
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue