Add endpoint to replace specific plugin
This commit is contained in:
parent
2ba1f46149
commit
9a21c6ade8
3 changed files with 100 additions and 21 deletions
|
@ -69,6 +69,45 @@ async def reload_plugin(request: web.Request) -> web.Response:
|
||||||
return resp.ok
|
return resp.ok
|
||||||
|
|
||||||
|
|
||||||
|
@routes.put("/plugin/{id}")
|
||||||
|
async def put_plugin(request: web.Request) -> web.Response:
|
||||||
|
plugin_id = request.match_info.get("id", None)
|
||||||
|
content = await request.read()
|
||||||
|
file = BytesIO(content)
|
||||||
|
try:
|
||||||
|
pid, version = ZippedPluginLoader.verify_meta(file)
|
||||||
|
except MaubotZipImportError as e:
|
||||||
|
return resp.plugin_import_error(str(e), traceback.format_exc())
|
||||||
|
if pid != plugin_id:
|
||||||
|
return resp.pid_mismatch
|
||||||
|
plugin = PluginLoader.id_cache.get(plugin_id, None)
|
||||||
|
if not plugin:
|
||||||
|
return await upload_new_plugin(content, pid, version)
|
||||||
|
elif isinstance(plugin, ZippedPluginLoader):
|
||||||
|
return await upload_replacement_plugin(plugin, content, version)
|
||||||
|
else:
|
||||||
|
return resp.unsupported_plugin_loader
|
||||||
|
|
||||||
|
|
||||||
|
@routes.post("/plugins/upload")
|
||||||
|
async def upload_plugin(request: web.Request) -> web.Response:
|
||||||
|
content = await request.read()
|
||||||
|
file = BytesIO(content)
|
||||||
|
try:
|
||||||
|
pid, version = ZippedPluginLoader.verify_meta(file)
|
||||||
|
except MaubotZipImportError as e:
|
||||||
|
return resp.plugin_import_error(str(e), traceback.format_exc())
|
||||||
|
plugin = PluginLoader.id_cache.get(pid, None)
|
||||||
|
if not plugin:
|
||||||
|
return await upload_new_plugin(content, pid, version)
|
||||||
|
elif not request.query.get("allow_override"):
|
||||||
|
return resp.plugin_exists
|
||||||
|
elif isinstance(plugin, ZippedPluginLoader):
|
||||||
|
return await upload_replacement_plugin(plugin, content, version)
|
||||||
|
else:
|
||||||
|
return resp.unsupported_plugin_loader
|
||||||
|
|
||||||
|
|
||||||
async def upload_new_plugin(content: bytes, pid: str, version: str) -> web.Response:
|
async def upload_new_plugin(content: bytes, pid: str, version: str) -> web.Response:
|
||||||
path = os.path.join(get_config()["plugin_directories.upload"], f"{pid}-v{version}.mbp")
|
path = os.path.join(get_config()["plugin_directories.upload"], f"{pid}-v{version}.mbp")
|
||||||
with open(path, "wb") as p:
|
with open(path, "wb") as p:
|
||||||
|
@ -86,10 +125,10 @@ async def upload_replacement_plugin(plugin: ZippedPluginLoader, content: bytes,
|
||||||
dirname = os.path.dirname(plugin.path)
|
dirname = os.path.dirname(plugin.path)
|
||||||
old_filename = os.path.basename(plugin.path)
|
old_filename = os.path.basename(plugin.path)
|
||||||
if plugin.version in old_filename:
|
if plugin.version in old_filename:
|
||||||
filename = old_filename.replace(plugin.version, new_version)
|
replacement = (new_version if plugin.version != new_version
|
||||||
if filename == old_filename:
|
else f"{new_version}-ts{int(time())}")
|
||||||
filename = re.sub(f"{re.escape(plugin.version)}(-ts[0-9]+)?",
|
filename = re.sub(f"{re.escape(plugin.version)}(-ts[0-9]+)?",
|
||||||
f"{new_version}-ts{int(time())}", old_filename)
|
replacement, old_filename)
|
||||||
else:
|
else:
|
||||||
filename = old_filename.rstrip(".mbp")
|
filename = old_filename.rstrip(".mbp")
|
||||||
filename = f"{filename}-v{new_version}.mbp"
|
filename = f"{filename}-v{new_version}.mbp"
|
||||||
|
@ -110,20 +149,3 @@ async def upload_replacement_plugin(plugin: ZippedPluginLoader, content: bytes,
|
||||||
await plugin.start_instances()
|
await plugin.start_instances()
|
||||||
ZippedPluginLoader.trash(old_path, reason="update")
|
ZippedPluginLoader.trash(old_path, reason="update")
|
||||||
return resp.updated(plugin.to_dict())
|
return resp.updated(plugin.to_dict())
|
||||||
|
|
||||||
|
|
||||||
@routes.post("/plugins/upload")
|
|
||||||
async def upload_plugin(request: web.Request) -> web.Response:
|
|
||||||
content = await request.read()
|
|
||||||
file = BytesIO(content)
|
|
||||||
try:
|
|
||||||
pid, version = ZippedPluginLoader.verify_meta(file)
|
|
||||||
except MaubotZipImportError as e:
|
|
||||||
return resp.plugin_import_error(str(e), traceback.format_exc())
|
|
||||||
plugin = PluginLoader.id_cache.get(pid, None)
|
|
||||||
if not plugin:
|
|
||||||
return await upload_new_plugin(content, pid, version)
|
|
||||||
elif isinstance(plugin, ZippedPluginLoader):
|
|
||||||
return await upload_replacement_plugin(plugin, content, version)
|
|
||||||
else:
|
|
||||||
return resp.unsupported_plugin_loader
|
|
||||||
|
|
|
@ -61,6 +61,13 @@ class _Response:
|
||||||
"errcode": "mxid_mismatch",
|
"errcode": "mxid_mismatch",
|
||||||
}, status=HTTPStatus.BAD_REQUEST)
|
}, status=HTTPStatus.BAD_REQUEST)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def pid_mismatch(self) -> web.Response:
|
||||||
|
return web.json_response({
|
||||||
|
"error": "The ID in the path does not match the ID of the uploaded plugin",
|
||||||
|
"errcode": "pid_mismatch",
|
||||||
|
}, status=HTTPStatus.BAD_REQUEST)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def bad_auth(self) -> web.Response:
|
def bad_auth(self) -> web.Response:
|
||||||
return web.json_response({
|
return web.json_response({
|
||||||
|
@ -138,6 +145,13 @@ class _Response:
|
||||||
"errcode": "user_exists",
|
"errcode": "user_exists",
|
||||||
}, status=HTTPStatus.CONFLICT)
|
}, status=HTTPStatus.CONFLICT)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def plugin_exists(self) -> web.Response:
|
||||||
|
return web.json_response({
|
||||||
|
"error": "A plugin with the same ID as the uploaded plugin already exists",
|
||||||
|
"errcode": "plugin_exists"
|
||||||
|
}, status=HTTPStatus.CONFLICT)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def plugin_in_use(self) -> web.Response:
|
def plugin_in_use(self) -> web.Response:
|
||||||
return web.json_response({
|
return web.json_response({
|
||||||
|
|
|
@ -85,6 +85,14 @@ paths:
|
||||||
summary: Upload a new plugin
|
summary: Upload a new plugin
|
||||||
description: Upload a new plugin. If the plugin already exists, enabled instances will be restarted.
|
description: Upload a new plugin. If the plugin already exists, enabled instances will be restarted.
|
||||||
tags: [Plugins]
|
tags: [Plugins]
|
||||||
|
parameters:
|
||||||
|
- name: allow_override
|
||||||
|
in: query
|
||||||
|
description: Set to allow overriding existing plugins
|
||||||
|
required: false
|
||||||
|
schema:
|
||||||
|
type: boolean
|
||||||
|
default: false
|
||||||
responses:
|
responses:
|
||||||
200:
|
200:
|
||||||
description: Plugin uploaded and replaced current version successfully
|
description: Plugin uploaded and replaced current version successfully
|
||||||
|
@ -102,6 +110,8 @@ paths:
|
||||||
$ref: '#/components/responses/BadRequest'
|
$ref: '#/components/responses/BadRequest'
|
||||||
401:
|
401:
|
||||||
$ref: '#/components/responses/Unauthorized'
|
$ref: '#/components/responses/Unauthorized'
|
||||||
|
409:
|
||||||
|
description: Plugin
|
||||||
requestBody:
|
requestBody:
|
||||||
content:
|
content:
|
||||||
application/zip:
|
application/zip:
|
||||||
|
@ -150,6 +160,39 @@ paths:
|
||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
$ref: '#/components/schemas/Error'
|
$ref: '#/components/schemas/Error'
|
||||||
|
put:
|
||||||
|
operationId: put_plugin
|
||||||
|
summary: Upload a new or replacement plugin
|
||||||
|
description: |
|
||||||
|
Upload a new or replacement plugin with the specified ID.
|
||||||
|
A HTTP 400 will be returned if the ID of the uploaded plugin
|
||||||
|
doesn't match the ID in the path. If the plugin already
|
||||||
|
exists, enabled instances will be restarted.
|
||||||
|
tags: [Plugins]
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: Plugin uploaded and replaced current version successfully
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/Plugin'
|
||||||
|
201:
|
||||||
|
description: New plugin uploaded successfully
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/Plugin'
|
||||||
|
400:
|
||||||
|
$ref: '#/components/responses/BadRequest'
|
||||||
|
401:
|
||||||
|
$ref: '#/components/responses/Unauthorized'
|
||||||
|
requestBody:
|
||||||
|
content:
|
||||||
|
application/zip:
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
format: binary
|
||||||
|
example: The plugin maubot archive (.mbp)
|
||||||
/plugin/{id}/reload:
|
/plugin/{id}/reload:
|
||||||
parameters:
|
parameters:
|
||||||
- name: id
|
- name: id
|
||||||
|
|
Loading…
Reference in a new issue