add admin api (w/ voice endpoints)

- add docs/admin_api.md
 - add litecord.voice_schemas
 - auth: add admin_check
 - voice.manager: add VoiceManager.voice_server_list
This commit is contained in:
Luna 2019-03-04 00:37:56 -03:00
parent b71aec5aa1
commit cb029b36e3
7 changed files with 216 additions and 3 deletions

45
docs/admin_api.md Normal file
View File

@ -0,0 +1,45 @@
# Litecord Admin API
the base path is `/api/v6/admin`.
## GET `/voice/regions/<region>`
Return a list of voice server objects for the region.
Returns empty list if the region does not exist.
| field | type | description |
| --: | :-- | :-- |
| hostname | string | the hostname of the voice server |
| last\_health | float | the health of the voice server |
## PUT `/voice/regions`
Create a voice region.
Receives JSON body as input, returns a list of voice region objects as output.
| field | type | description |
| --: | :-- | :-- |
| id | string | id of the voice region, "brazil", "us-east", "eu-west", etc |
| name | string | name of the voice region |
| vip | Optional[bool] | if voice region is vip-only, default false |
| deprecated | Optional[bool] | if voice region is deprecated, default false |
| custom | Optional[bool] | if voice region is custom-only, default false |
## PUT `/voice/regions/<region>/server`
Create a voice server for a region.
Returns empty body with 204 status code on success.
| field | type | description |
| --: | :-- | :-- |
| hostname | string | the hostname of the voice server |
## PUT `/voice/regions/<region>/deprecate`
Mark a voice region as deprecated. Disables any voice actions on guilds that are
using the voice region.
Returns empty body with 204 status code on success.

31
litecord/admin_schemas.py Normal file
View File

@ -0,0 +1,31 @@
"""
Litecord
Copyright (C) 2018-2019 Luna Mendes
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, version 3 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
VOICE_SERVER = {
'hostname': {'type': 'string', 'maxlength': 255, 'required': True}
}
VOICE_REGION = {
'id': {'type': 'string', 'maxlength': 255, 'required': True},
'name': {'type': 'string', 'maxlength': 255, 'required': True},
'vip': {'type': 'boolean', 'default': False},
'deprecated': {'type': 'boolean', 'default': False},
'custom': {'type': 'boolean', 'default': False},
}

View File

@ -29,6 +29,7 @@ from quart import request, current_app as app
from litecord.errors import Forbidden, Unauthorized, BadRequest
from litecord.snowflake import get_snowflake
from litecord.enums import UserFlags
log = Logger(__name__)
@ -100,6 +101,23 @@ async def token_check():
return user_id
async def admin_check():
"""Check if the user is an admin."""
user_id = await token_check()
flags = await app.db.fetchval("""
SELECT flags
FROM users
WHERE id = $1
""", user_id)
flags = UserFlags.from_int(flags)
if not flags.is_staff:
raise Unauthorized('you are not staff')
return user_id
async def hash_data(data: str, loop=None) -> str:
"""Hash information with bcrypt."""
loop = loop or app.loop

View File

@ -0,0 +1,22 @@
"""
Litecord
Copyright (C) 2018-2019 Luna Mendes
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, version 3 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
from .voice import bp as voice
__all__ = ['voice']

View File

@ -0,0 +1,83 @@
"""
Litecord
Copyright (C) 2018-2019 Luna Mendes
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, version 3 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
from quart import Blueprint, jsonify, current_app as app, request
from litecord.auth import admin_check
from litecord.schemas import validate
from litecord.admin_schemas import VOICE_SERVER, VOICE_REGION
bp = Blueprint('voice_admin', __name__)
@bp.route('/regions/<region>', methods=['GET'])
async def get_region_servers(region):
"""Return a list of all servers for a region."""
_user_id = await admin_check()
servers = await app.voice.voice_server_list(region)
return jsonify(servers)
@bp.route('/regions', methods=['PUT'])
async def insert_new_region():
"""Create a voice region."""
_user_id = await admin_check()
j = validate(await request.get_json(), VOICE_REGION)
j['id'] = j['id'].lower()
await app.db.execute("""
INSERT INTO voice_regions (id, name, vip, deprecated, custom)
VALUES ($1, $2, $3, $4, $5)
""", j['id'], j['name'], j['vip'], j['deprecated'], j['custom'])
return jsonify(
await app.storage.all_voice_regions()
)
@bp.route('/regions/<region>/servers', methods=['PUT'])
async def put_region_server(region):
"""Insert a voice server to a region"""
_user_id = await admin_check()
j = validate(await request.get_json(), VOICE_SERVER)
await app.db.execute("""
INSERT INTO voice_servers (hostname, region)
VALUES ($1, $2)
""", j['hostname'], region)
return '', 204
@bp.route('/regions/<region>/deprecate', methods=['PUT'])
async def deprecate_region(region):
"""Deprecate a voice region."""
_user_id = await admin_check()
# TODO: write this
await app.voice.disable_region(region)
await app.db.execute("""
UPDATE voice_regions
SET deprecated = true
WHERE id = $1
""", region)
return '', 204

View File

@ -181,3 +181,13 @@ class VoiceManager:
async def leave(self, guild_id: int, user_id: int):
"""Make a user leave a channel IN A GUILD."""
await self.del_state((guild_id, user_id))
async def voice_server_list(self, region: str):
"""Get a list of voice server objects"""
rows = await self.app.db.fetch("""
SELECT hostname, last_health
FROM voice_servers
WHERE region_id = $1
""", region)
return list(map(dict, rows))

10
run.py
View File

@ -54,8 +54,10 @@ from litecord.blueprints.user import (
user_settings, user_billing, fake_store
)
from litecord.blueprints.user.billing_job import (
payment_job
from litecord.blueprints.user.billing_job import payment_job
from litecord.blueprints.admin_api import (
voice as voice_admin
)
from litecord.ratelimits.handler import ratelimit_handler
@ -137,7 +139,9 @@ def set_blueprints(app_):
icons: -1,
attachments: -1,
nodeinfo: -1,
static: -1
static: -1,
voice_admin: '/admin/voice'
}
for bp, suffix in bps.items():