From 78aba9432e1f6b89098ba59259febc7430fd5117 Mon Sep 17 00:00:00 2001 From: Luna Date: Sat, 16 Mar 2019 17:23:00 -0300 Subject: [PATCH] admin_api.instance_invites add impls for other endpoints - docs/admin_api.md: document them - docs/admin_api.md: add basic usage of the api --- docs/admin_api.md | 34 +++++++- litecord/admin_schemas.py | 4 + .../blueprints/admin_api/instance_invites.py | 77 ++++++++++++++++++- 3 files changed, 109 insertions(+), 6 deletions(-) diff --git a/docs/admin_api.md b/docs/admin_api.md index ab28751..2a5f7c4 100644 --- a/docs/admin_api.md +++ b/docs/admin_api.md @@ -1,6 +1,13 @@ # Litecord Admin API -the base path is `/api/v6/admin`. +Litecord's Admin API uses the same authentication methods as Discord, +it's the same `Authorization` header, and the same token. + +Only users who have the staff flag set can use the Admin API. Instance +owners can use the `./manage.py make_staff` manage task to set someone +as a staff user, granting them access over the administration functions. + +The base path is `/api/v6/admin`. ## User management @@ -27,17 +34,36 @@ Search users. ## Instance invites +Instance invites are used for instances that do not have open +registrations but want to let some people in regardless. Users +go to the `/invite_register.html` page in the instance and put +their data in. + +### Instance Invite object + +| field | type | description | +| --: | :-- | :-- | +| code | string | instance invite code | +| created\_at | ISO8907 timestamp | when the invite was created | +| max\_uses | integer | maximum amount of uses | +| uses | integer | how many times has the invite been used | + ### `GET /instance/invites` -**TODO** +Get a list of instance invites. ### `PUT /instance/invites` -**TODO** +Create an instance invite. Receives only the `max_uses` +field from the instance invites object. Returns a full +instance invite object. ### `DELETE /instance/invites/` -**TODO** +Delete an invite. Does not have any input, only the instance invite's `code` +as the `` parameter in the URL. + +Returns empty body 204 on success, 404 on invite not found. ## Voice diff --git a/litecord/admin_schemas.py b/litecord/admin_schemas.py index 8f2a001..d4dba63 100644 --- a/litecord/admin_schemas.py +++ b/litecord/admin_schemas.py @@ -46,3 +46,7 @@ USER_CREATE = { 'email': {'type': 'email', 'required': True}, 'password': {'type': 'string', 'minlength': 5, 'required': True}, } + +INSTANCE_INVITE = { + 'max_uses': {'type': 'integer', 'required': True} +} diff --git a/litecord/blueprints/admin_api/instance_invites.py b/litecord/blueprints/admin_api/instance_invites.py index 70c6106..87279c1 100644 --- a/litecord/blueprints/admin_api/instance_invites.py +++ b/litecord/blueprints/admin_api/instance_invites.py @@ -17,11 +17,40 @@ along with this program. If not, see . """ -from quart import Blueprint, jsonify, current_app as app +import string +from random import choice + +from quart import Blueprint, jsonify, current_app as app, request from litecord.auth import admin_check +from litecord.types import timestamp_ +from litecord.schemas import validate +from litecord.admin_schemas import INSTANCE_INVITE bp = Blueprint('instance_invites', __name__) +ALPHABET = string.ascii_lowercase + string.ascii_uppercase + string.digits + + +async def _gen_inv() -> str: + """Generate an invite code""" + return ''.join(choice(ALPHABET) for _ in range(6)) + + +async def gen_inv(ctx) -> str: + """Generate an invite.""" + for _ in range(10): + possible_inv = await _gen_inv() + + created_at = await ctx.db.fetchval(""" + SELECT created_at + FROM instance_invites + WHERE code = $1 + """, possible_inv) + + if created_at is None: + return possible_inv + + return None @bp.route('', methods=['GET']) @@ -33,4 +62,48 @@ async def _all_instance_invites(): FROM instance_invites """) - return jsonify([dict(row) for row in rows]) + rows = [dict(row) for row in rows] + + for row in rows: + row['timestamp'] = timestamp_(row['timestamp']) + + return jsonify(rows) + + +@bp.route('', methods=['PUT']) +async def _create_invite(): + await admin_check() + + code = await gen_inv(app) + if not code: + return 'failed to make invite', 500 + + j = validate(await request.get_json(), INSTANCE_INVITE) + + await app.db.execute(""" + INSERT INTO instance_invites (code, max_uses) + VALUES ($1, $2) + """, code, j['max_uses']) + + inv = dict(await app.db.fetchrow(""" + SELECT code, created_at, uses, max_uses + FROM instance_invites + WHERE code = $1 + """, code)) + + return jsonify(dict(inv)) + + +@bp.route('/', methods=['DELETE']) +async def _del_invite(invite: str): + await admin_check() + + res = await app.db.execute(""" + DELETE FROM instance_invites + WHERE code = $1 + """, invite) + + if res.lower() == 'update 0': + return 'invite not found', 404 + + return '', 204