from pathlib import PosixPath from flask import Blueprint, abort, render_template, redirect from flask.helpers import url_for from flask_login import current_user, login_required from flask_restful import Api, Resource from flask_wtf.form import FlaskForm from wtforms.fields import SubmitField, StringField from wtforms.validators import DataRequired, ValidationError from .db import APIKey, db blueprint = Blueprint('api', __name__) api = Api() def init_app(app): api.init_app(app) def colon_checker(form, field): if ":" in field.data: raise ValidationError('Field cannot contain colons') class NewKeyForm(FlaskForm): app_name = StringField('App name', validators=[ colon_checker, DataRequired()]) description = StringField('Description', validators=[ colon_checker, DataRequired()]) submit = SubmitField('Create') @blueprint.route('/api/manage/keys', methods=['GET', 'POST']) @login_required def key_list(): apps = [a[0] for a in db.session.query(APIKey.app_name).filter( APIKey.user == current_user).group_by(APIKey.app_name).all()] keys = {app_name: APIKey.query.filter( APIKey.user_id == current_user.id, APIKey.app_name == app_name).all() for app_name in apps} form = NewKeyForm() if form.validate_on_submit(): key = APIKey.create(current_user, form.app_name.data, form.description.data) return render_template('api/manage/key_list.html', form=NewKeyForm(), apps=sorted(apps), keys=keys, created_key=key) return render_template('api/manage/key_list.html', form=form, apps=sorted(apps), keys=keys) class DeleteForm(FlaskForm): submit = SubmitField('Delete') @blueprint.route('/api/manage/keys//delete', methods=['GET', 'POST']) @login_required def key_delete(key_id): k = APIKey.query.get_or_404(key_id) if k.user_id != current_user.id: abort(403) back_url = url_for('api.key_list') form = DeleteForm() if form.validate_on_submit(): db.session.delete(k) db.session.commit() return redirect(back_url) return render_template('api/manage/key_delete.html', form=form, key=k, cancel=back_url) def r(resource, endpoint: str): api.add_resource( resource, str(PosixPath('/api/v1') / endpoint)) s = {'success': True} def e(err): return {'success': False, 'error': err} class Version(Resource): def get(self): return s | {'version': 'v1'} r(Version, '')