mirror of
				https://gitlab.ub.uni-bielefeld.de/sfb1288inf/nopaque.git
				synced 2025-11-04 04:12:45 +00:00 
			
		
		
		
	move blueprints in dedicated folder
This commit is contained in:
		
							
								
								
									
										14
									
								
								app/blueprints/api/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								app/blueprints/api/__init__.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,14 @@
 | 
			
		||||
from flask import Blueprint
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
bp = Blueprint('api', __name__)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
from .tokens import bp as tokens_blueprint
 | 
			
		||||
bp.register_blueprint(tokens_blueprint, url_prefix='/tokens')
 | 
			
		||||
 | 
			
		||||
from .users import bp as users_blueprint
 | 
			
		||||
bp.register_blueprint(users_blueprint, url_prefix='/users')
 | 
			
		||||
 | 
			
		||||
from .jobs import bp as jobs_blueprint
 | 
			
		||||
bp.register_blueprint(jobs_blueprint, url_prefix='/jobs')
 | 
			
		||||
							
								
								
									
										48
									
								
								app/blueprints/api/auth.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								app/blueprints/api/auth.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,48 @@
 | 
			
		||||
from flask_httpauth import HTTPBasicAuth, HTTPTokenAuth
 | 
			
		||||
from werkzeug.exceptions import Forbidden, Unauthorized
 | 
			
		||||
from app.models import User
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
basic_auth = HTTPBasicAuth()
 | 
			
		||||
token_auth = HTTPTokenAuth()
 | 
			
		||||
auth_error_responses = {
 | 
			
		||||
    Unauthorized.code: Unauthorized.description,
 | 
			
		||||
    Forbidden.code: Forbidden.description
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@basic_auth.verify_password
 | 
			
		||||
def verify_password(email_or_username, password):
 | 
			
		||||
    user = User.query.filter((User.email == email_or_username.lower()) | (User.username == email_or_username)).first()
 | 
			
		||||
    if user is not None and user.verify_password(password):
 | 
			
		||||
        return user
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@basic_auth.error_handler
 | 
			
		||||
def basic_auth_error(status):
 | 
			
		||||
    error = (Forbidden if status == 403 else Unauthorized)()
 | 
			
		||||
    return {
 | 
			
		||||
        'code': error.code,
 | 
			
		||||
        'message': error.name,
 | 
			
		||||
        'description': error.description,
 | 
			
		||||
    }, error.code, {'WWW-Authenticate': 'Form'}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@token_auth.verify_token
 | 
			
		||||
def verify_token(token):
 | 
			
		||||
    return User.verify_access_token(token) if token else None
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@token_auth.error_handler
 | 
			
		||||
def token_auth_error(status):
 | 
			
		||||
    error = (Forbidden if status == 403 else Unauthorized)()
 | 
			
		||||
    return {
 | 
			
		||||
        'code': error.code,
 | 
			
		||||
        'message': error.name,
 | 
			
		||||
        'description': error.description,
 | 
			
		||||
    }, error.code
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@basic_auth.get_user_roles
 | 
			
		||||
@token_auth.get_user_roles
 | 
			
		||||
def get_user_roles(user):
 | 
			
		||||
    return [user.role.name]
 | 
			
		||||
							
								
								
									
										102
									
								
								app/blueprints/api/jobs.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										102
									
								
								app/blueprints/api/jobs.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,102 @@
 | 
			
		||||
 | 
			
		||||
from apifairy import authenticate, response
 | 
			
		||||
from apifairy.decorators import body, other_responses
 | 
			
		||||
from flask import abort, Blueprint
 | 
			
		||||
from werkzeug.exceptions import InternalServerError
 | 
			
		||||
from app import db, hashids
 | 
			
		||||
from app.models import Job, JobInput, JobStatus, TesseractOCRPipelineModel
 | 
			
		||||
from .auth import auth_error_responses, token_auth
 | 
			
		||||
from .schemas import EmptySchema, JobSchema, SpaCyNLPPipelineJobSchema, TesseractOCRPipelineJobSchema, TesseractOCRPipelineModelSchema
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
bp = Blueprint('jobs', __name__)
 | 
			
		||||
job_schema = JobSchema()
 | 
			
		||||
jobs_schema = JobSchema(many=True)
 | 
			
		||||
spacy_nlp_pipeline_job_schema = SpaCyNLPPipelineJobSchema()
 | 
			
		||||
tesseract_ocr_pipeline_job_schema = TesseractOCRPipelineJobSchema()
 | 
			
		||||
tesseract_ocr_pipeline_model_schema = TesseractOCRPipelineModelSchema()
 | 
			
		||||
tesseract_ocr_pipeline_models_schema = TesseractOCRPipelineModelSchema(many=True)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@bp.route('', methods=['GET'])
 | 
			
		||||
@authenticate(token_auth, role='Administrator')
 | 
			
		||||
@response(jobs_schema)
 | 
			
		||||
@other_responses(auth_error_responses)
 | 
			
		||||
def get_jobs():
 | 
			
		||||
    """Get all jobs"""
 | 
			
		||||
    return Job.query.all()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@bp.route('/tesseract-ocr-pipeline', methods=['POST'])
 | 
			
		||||
@authenticate(token_auth)
 | 
			
		||||
@body(tesseract_ocr_pipeline_job_schema, location='form')
 | 
			
		||||
@response(job_schema)
 | 
			
		||||
@other_responses({**auth_error_responses, InternalServerError.code: InternalServerError.description})
 | 
			
		||||
def create_tesseract_ocr_pipeline_job(args):
 | 
			
		||||
    """Create a new Tesseract OCR Pipeline job"""
 | 
			
		||||
    current_user = token_auth.current_user()
 | 
			
		||||
    try:
 | 
			
		||||
        job = Job.create(
 | 
			
		||||
            title=args['title'],
 | 
			
		||||
            description=args['description'],
 | 
			
		||||
            service='tesseract-ocr-pipeline',
 | 
			
		||||
            service_args={
 | 
			
		||||
                'model': hashids.decode(args['model_id']),
 | 
			
		||||
                'binarization': args['binarization']
 | 
			
		||||
            },
 | 
			
		||||
            service_version=args['service_version'],
 | 
			
		||||
            user=current_user
 | 
			
		||||
        )
 | 
			
		||||
    except OSError:
 | 
			
		||||
        abort(500)
 | 
			
		||||
    try:
 | 
			
		||||
        JobInput.create(args['pdf'], job=job)
 | 
			
		||||
    except OSError:
 | 
			
		||||
        abort(500)
 | 
			
		||||
    job.status = JobStatus.SUBMITTED
 | 
			
		||||
    db.session.commit()
 | 
			
		||||
    return job, 201
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@bp.route('/tesseract-ocr-pipeline/models', methods=['GET'])
 | 
			
		||||
@authenticate(token_auth)
 | 
			
		||||
@response(tesseract_ocr_pipeline_models_schema)
 | 
			
		||||
@other_responses(auth_error_responses)
 | 
			
		||||
def get_tesseract_ocr_models():
 | 
			
		||||
    """Get all Tesseract OCR Models"""
 | 
			
		||||
    return TesseractOCRPipelineModel.query.all()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@bp.route('/<hashid:job_id>', methods=['DELETE'])
 | 
			
		||||
@authenticate(token_auth)
 | 
			
		||||
@response(EmptySchema, status_code=204)
 | 
			
		||||
@other_responses(auth_error_responses)
 | 
			
		||||
def delete_job(job_id):
 | 
			
		||||
    """Delete a job by id"""
 | 
			
		||||
    current_user = token_auth.current_user()
 | 
			
		||||
    job = Job.query.get(job_id)
 | 
			
		||||
    if job is None:
 | 
			
		||||
        abort(404)
 | 
			
		||||
    if not (job.user == current_user or current_user.is_administrator):
 | 
			
		||||
        abort(403)
 | 
			
		||||
    try:
 | 
			
		||||
        job.delete()
 | 
			
		||||
    except OSError as e:
 | 
			
		||||
        abort(500)
 | 
			
		||||
    db.session.commit()
 | 
			
		||||
    return {}, 204
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@bp.route('/<hashid:job_id>', methods=['GET'])
 | 
			
		||||
@authenticate(token_auth)
 | 
			
		||||
@response(job_schema)
 | 
			
		||||
@other_responses(auth_error_responses)
 | 
			
		||||
def get_job(job_id):
 | 
			
		||||
    """Get a job by id"""
 | 
			
		||||
    current_user = token_auth.current_user()
 | 
			
		||||
    job = Job.query.get(job_id)
 | 
			
		||||
    if job is None:
 | 
			
		||||
        abort(404)
 | 
			
		||||
    if not (job.user == current_user or current_user.is_administrator):
 | 
			
		||||
        abort(403)
 | 
			
		||||
    return job
 | 
			
		||||
							
								
								
									
										173
									
								
								app/blueprints/api/schemas.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										173
									
								
								app/blueprints/api/schemas.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,173 @@
 | 
			
		||||
from apifairy.fields import FileField
 | 
			
		||||
from marshmallow import validate, validates, ValidationError
 | 
			
		||||
from marshmallow.decorators import post_dump
 | 
			
		||||
from app import ma
 | 
			
		||||
from app.models import (
 | 
			
		||||
    Job,
 | 
			
		||||
    JobStatus,
 | 
			
		||||
    TesseractOCRPipelineModel,
 | 
			
		||||
    Token,
 | 
			
		||||
    User,
 | 
			
		||||
    UserSettingJobStatusMailNotificationLevel
 | 
			
		||||
)
 | 
			
		||||
from app.blueprints.services import SERVICES
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class EmptySchema(ma.Schema):
 | 
			
		||||
    pass
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TokenSchema(ma.SQLAlchemySchema):
 | 
			
		||||
    class Meta:
 | 
			
		||||
        model = Token
 | 
			
		||||
        ordered = True
 | 
			
		||||
 | 
			
		||||
    access_token = ma.String(required=True)
 | 
			
		||||
    refresh_token = ma.String()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TesseractOCRPipelineModelSchema(ma.SQLAlchemySchema):
 | 
			
		||||
    class Meta:
 | 
			
		||||
        model = TesseractOCRPipelineModel
 | 
			
		||||
        ordered = True
 | 
			
		||||
 | 
			
		||||
    hashid = ma.String(data_key='id', dump_only=True)
 | 
			
		||||
    user_hashid = ma.String(data_key='user_id', dump_only=True)
 | 
			
		||||
    title = ma.auto_field(
 | 
			
		||||
        required=True,
 | 
			
		||||
        validate=validate.Length(min=1, max=64)
 | 
			
		||||
    )
 | 
			
		||||
    description = ma.auto_field(
 | 
			
		||||
        required=True,
 | 
			
		||||
        validate=validate.Length(min=1, max=255)
 | 
			
		||||
    )
 | 
			
		||||
    version = ma.String(
 | 
			
		||||
        required=True,
 | 
			
		||||
        validate=validate.Length(min=1, max=16)
 | 
			
		||||
    )
 | 
			
		||||
    compatible_service_versions = ma.List(
 | 
			
		||||
        ma.String(required=True, validate=validate.Length(min=1, max=16)),
 | 
			
		||||
        required=True,
 | 
			
		||||
        validate=validate.Length(min=1, max=255)
 | 
			
		||||
    )
 | 
			
		||||
    publisher = ma.String(
 | 
			
		||||
        required=True,
 | 
			
		||||
        validate=validate.Length(min=1, max=128)
 | 
			
		||||
    )
 | 
			
		||||
    publisher_url = ma.String(
 | 
			
		||||
        validate=[validate.URL(), validate.Length(min=1, max=512)]
 | 
			
		||||
    )
 | 
			
		||||
    publishing_url = ma.String(
 | 
			
		||||
        required=True,
 | 
			
		||||
        validate=[validate.URL(), validate.Length(min=1, max=512)]
 | 
			
		||||
    )
 | 
			
		||||
    publishing_year = ma.Int(
 | 
			
		||||
        required=True
 | 
			
		||||
    )
 | 
			
		||||
    is_public = ma.Boolean(required=True)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class JobSchema(ma.SQLAlchemySchema):
 | 
			
		||||
    class Meta:
 | 
			
		||||
        model = Job
 | 
			
		||||
        ordered = True
 | 
			
		||||
 | 
			
		||||
    hashid = ma.String(data_key='id', dump_only=True)
 | 
			
		||||
    user_hashid = ma.String(data_key='user_id', dump_only=True)
 | 
			
		||||
    title = ma.auto_field(
 | 
			
		||||
        required=True,
 | 
			
		||||
        validate=validate.Length(min=1, max=32)
 | 
			
		||||
    )
 | 
			
		||||
    description = ma.auto_field(
 | 
			
		||||
        required=True,
 | 
			
		||||
        validate=validate.Length(min=1, max=255)
 | 
			
		||||
    )
 | 
			
		||||
    creation_date = ma.auto_field(dump_only=True)
 | 
			
		||||
    end_date = ma.auto_field(dump_only=True)
 | 
			
		||||
    service = ma.String(
 | 
			
		||||
        dump_only=True,
 | 
			
		||||
        validate=validate.OneOf(SERVICES.keys())
 | 
			
		||||
    )
 | 
			
		||||
    service_args = ma.Dict(dump_only=True)
 | 
			
		||||
    service_version = ma.String(dump_only=True)
 | 
			
		||||
    status = ma.String(
 | 
			
		||||
        dump_only=True,
 | 
			
		||||
        validate=validate.OneOf(list(JobStatus.__members__.keys()))
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    @post_dump(pass_original=True)
 | 
			
		||||
    def post_dump(self, serialized_job, job, **kwargs):
 | 
			
		||||
        serialized_job['status'] = job.status.name
 | 
			
		||||
        return serialized_job
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TesseractOCRPipelineJobSchema(JobSchema):
 | 
			
		||||
    binarization = ma.Boolean(load_only=True, missing=False)
 | 
			
		||||
    model_id = ma.String(required=True, load_only=True)
 | 
			
		||||
    service_version = ma.auto_field(
 | 
			
		||||
        required=True,
 | 
			
		||||
        validate=[validate.Length(min=1, max=16), validate.OneOf(list(SERVICES['tesseract-ocr-pipeline']['versions'].keys()))]
 | 
			
		||||
    )
 | 
			
		||||
    pdf = FileField()
 | 
			
		||||
 | 
			
		||||
    @validates('pdf')
 | 
			
		||||
    def validate_pdf(self, value):
 | 
			
		||||
        if value.mimetype != 'application/pdf':
 | 
			
		||||
            raise ValidationError('PDF files only!')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class SpaCyNLPPipelineJobSchema(JobSchema):
 | 
			
		||||
    binarization = ma.Boolean(load_only=True, missing=False)
 | 
			
		||||
    model_id = ma.String(required=True, load_only=True)
 | 
			
		||||
    service_version = ma.auto_field(
 | 
			
		||||
        required=True,
 | 
			
		||||
        validate=[validate.Length(min=1, max=16), validate.OneOf(list(SERVICES['tesseract-ocr-pipeline']['versions'].keys()))]
 | 
			
		||||
    )
 | 
			
		||||
    txt = FileField(required=True)
 | 
			
		||||
 | 
			
		||||
    @validates('txt')
 | 
			
		||||
    def validate_txt(self, value):
 | 
			
		||||
        if value.mimetype != 'text/plain':
 | 
			
		||||
            raise ValidationError('Plain text files only!')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class UserSchema(ma.SQLAlchemySchema):
 | 
			
		||||
    class Meta:
 | 
			
		||||
        model = User
 | 
			
		||||
        ordered = True
 | 
			
		||||
 | 
			
		||||
    hashid = ma.String(data_key='id', dump_only=True)
 | 
			
		||||
    username = ma.auto_field(
 | 
			
		||||
        validate=[
 | 
			
		||||
            validate.Length(min=1, max=64),
 | 
			
		||||
            validate.Regexp(
 | 
			
		||||
                User.username_pattern,
 | 
			
		||||
                error='Usernames must have only letters, numbers, dots or underscores'
 | 
			
		||||
            )
 | 
			
		||||
        ]
 | 
			
		||||
    )
 | 
			
		||||
    email = ma.auto_field(validate=validate.Email())
 | 
			
		||||
    member_since = ma.auto_field(dump_only=True)
 | 
			
		||||
    last_seen = ma.auto_field(dump_only=True)
 | 
			
		||||
    password = ma.String(load_only=True)
 | 
			
		||||
    last_seen = ma.auto_field(dump_only=True)
 | 
			
		||||
    setting_job_status_mail_notification_level = ma.String(
 | 
			
		||||
        validate=validate.OneOf(list(UserSettingJobStatusMailNotificationLevel.__members__.keys()))
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    @validates('email')
 | 
			
		||||
    def validate_email(self, email):
 | 
			
		||||
        if User.query.filter(User.email == email).first():
 | 
			
		||||
            raise ValidationError('Email already registered')
 | 
			
		||||
 | 
			
		||||
    @validates('username')
 | 
			
		||||
    def validate_username(self, username):
 | 
			
		||||
        if User.query.filter(User.username == username).first():
 | 
			
		||||
            raise ValidationError('Username already in use')
 | 
			
		||||
 | 
			
		||||
    @post_dump(pass_original=True)
 | 
			
		||||
    def post_dump(self, serialized_user, user, **kwargs):
 | 
			
		||||
        serialized_user['setting_job_status_mail_notification_level'] = \
 | 
			
		||||
            user.setting_job_status_mail_notification_level.name
 | 
			
		||||
        return serialized_user
 | 
			
		||||
							
								
								
									
										58
									
								
								app/blueprints/api/tokens.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								app/blueprints/api/tokens.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,58 @@
 | 
			
		||||
from apifairy import authenticate, body, response, other_responses
 | 
			
		||||
from flask import Blueprint, request, abort
 | 
			
		||||
from app import db
 | 
			
		||||
from app.models import Token, User
 | 
			
		||||
from .auth import basic_auth
 | 
			
		||||
from .schemas import EmptySchema, TokenSchema
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
bp = Blueprint('tokens', __name__)
 | 
			
		||||
token_schema = TokenSchema()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@bp.route('', methods=['DELETE'])
 | 
			
		||||
@response(EmptySchema, status_code=204, description='Token revoked')
 | 
			
		||||
@other_responses({401: 'Invalid access token'})
 | 
			
		||||
def delete_token():
 | 
			
		||||
    """Revoke an access token"""
 | 
			
		||||
    access_token = request.headers['Authorization'].split()[1]
 | 
			
		||||
    token = Token.query.filter(Token.access_token == access_token).first()
 | 
			
		||||
    if token is None:  # pragma: no cover
 | 
			
		||||
        abort(401)
 | 
			
		||||
    token.expire()
 | 
			
		||||
    db.session.commit()
 | 
			
		||||
    return {}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@bp.route('', methods=['POST'])
 | 
			
		||||
@authenticate(basic_auth)
 | 
			
		||||
@response(token_schema)
 | 
			
		||||
@other_responses({401: 'Invalid username or password'})
 | 
			
		||||
def create_token():
 | 
			
		||||
    """Create new access and refresh tokens"""
 | 
			
		||||
    user = basic_auth.current_user()
 | 
			
		||||
    token = user.generate_auth_token()
 | 
			
		||||
    db.session.add(token)
 | 
			
		||||
    Token.clean()  # keep token table clean of old tokens
 | 
			
		||||
    db.session.commit()
 | 
			
		||||
    return token, 200
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@bp.route('', methods=['PUT'])
 | 
			
		||||
@body(token_schema)
 | 
			
		||||
@response(token_schema, description='Newly issued access and refresh tokens')
 | 
			
		||||
@other_responses({401: 'Invalid access or refresh token'})
 | 
			
		||||
def refresh_token(args):
 | 
			
		||||
    """Refresh an access token"""
 | 
			
		||||
    access_token = args.get('access_token')
 | 
			
		||||
    refresh_token = args.get('refresh_token')
 | 
			
		||||
    if access_token is None or refresh_token is None:
 | 
			
		||||
        abort(401)
 | 
			
		||||
    token = User.verify_refresh_token(refresh_token, access_token)
 | 
			
		||||
    if token is None:
 | 
			
		||||
        abort(401)
 | 
			
		||||
    token.expire()
 | 
			
		||||
    new_token = token.user.generate_auth_token()
 | 
			
		||||
    db.session.add_all([token, new_token])
 | 
			
		||||
    db.session.commit()
 | 
			
		||||
    return new_token, 200
 | 
			
		||||
							
								
								
									
										99
									
								
								app/blueprints/api/users.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										99
									
								
								app/blueprints/api/users.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,99 @@
 | 
			
		||||
 | 
			
		||||
from apifairy import authenticate, body, response
 | 
			
		||||
from apifairy.decorators import other_responses
 | 
			
		||||
from flask import abort, Blueprint
 | 
			
		||||
from werkzeug.exceptions import InternalServerError
 | 
			
		||||
from app.email import create_message, send
 | 
			
		||||
from app import db
 | 
			
		||||
from app.models import User
 | 
			
		||||
from .auth import auth_error_responses, token_auth
 | 
			
		||||
from .schemas import EmptySchema, UserSchema
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
bp = Blueprint('users', __name__)
 | 
			
		||||
user_schema = UserSchema()
 | 
			
		||||
users_schema = UserSchema(many=True)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@bp.route('', methods=['GET'])
 | 
			
		||||
@authenticate(token_auth, role='Administrator')
 | 
			
		||||
@response(users_schema)
 | 
			
		||||
@other_responses(auth_error_responses)
 | 
			
		||||
def get_users():
 | 
			
		||||
    """Get all users"""
 | 
			
		||||
    return User.query.all()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@bp.route('', methods=['POST'])
 | 
			
		||||
@body(user_schema)
 | 
			
		||||
@response(user_schema, 201)
 | 
			
		||||
@other_responses({InternalServerError.code: InternalServerError.description})
 | 
			
		||||
def create_user(args):
 | 
			
		||||
    """Create a new user"""
 | 
			
		||||
    try:
 | 
			
		||||
        user = User.create(
 | 
			
		||||
            email=args['email'].lower(),
 | 
			
		||||
            password=args['password'],
 | 
			
		||||
            username=args['username']
 | 
			
		||||
        )
 | 
			
		||||
    except OSError:
 | 
			
		||||
        abort(500)
 | 
			
		||||
    msg = create_message(
 | 
			
		||||
        user.email,
 | 
			
		||||
        'Confirm Your Account',
 | 
			
		||||
        'auth/email/confirm',
 | 
			
		||||
        token=user.generate_confirm_token(),
 | 
			
		||||
        user=user
 | 
			
		||||
    )
 | 
			
		||||
    send(msg)
 | 
			
		||||
    db.session.commit()
 | 
			
		||||
    return user, 201
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@bp.route('/<hashid:user_id>', methods=['DELETE'])
 | 
			
		||||
@authenticate(token_auth)
 | 
			
		||||
@response(EmptySchema, status_code=204)
 | 
			
		||||
@other_responses(auth_error_responses)
 | 
			
		||||
def delete_user(user_id):
 | 
			
		||||
    """Delete a user by id"""
 | 
			
		||||
    current_user = token_auth.current_user()
 | 
			
		||||
    user = User.query.get(user_id)
 | 
			
		||||
    if user is None:
 | 
			
		||||
        abort(404)
 | 
			
		||||
    if not (user == current_user or current_user.is_administrator):
 | 
			
		||||
        abort(403)
 | 
			
		||||
    user.delete()
 | 
			
		||||
    db.session.commit()
 | 
			
		||||
    return {}, 204
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@bp.route('/<hashid:user_id>', methods=['GET'])
 | 
			
		||||
@authenticate(token_auth)
 | 
			
		||||
@response(user_schema)
 | 
			
		||||
@other_responses(auth_error_responses)
 | 
			
		||||
@other_responses({404: 'User not found'})
 | 
			
		||||
def get_user(user_id):
 | 
			
		||||
    """Retrieve a user by id"""
 | 
			
		||||
    current_user = token_auth.current_user()
 | 
			
		||||
    user = User.query.get(user_id)
 | 
			
		||||
    if user is None:
 | 
			
		||||
        abort(404)
 | 
			
		||||
    if not (user == current_user or current_user.is_administrator):
 | 
			
		||||
        abort(403)
 | 
			
		||||
    return user
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@bp.route('/<username>', methods=['GET'])
 | 
			
		||||
@authenticate(token_auth)
 | 
			
		||||
@response(user_schema)
 | 
			
		||||
@other_responses(auth_error_responses)
 | 
			
		||||
@other_responses({404: 'User not found'})
 | 
			
		||||
def get_user_by_username(username):
 | 
			
		||||
    """Retrieve a user by username"""
 | 
			
		||||
    current_user = token_auth.current_user()
 | 
			
		||||
    user = User.query.filter(User.username == username).first()
 | 
			
		||||
    if user is None:
 | 
			
		||||
        abort(404)
 | 
			
		||||
    if not (user == current_user or current_user.is_administrator):
 | 
			
		||||
        abort(403)
 | 
			
		||||
    return user
 | 
			
		||||
		Reference in New Issue
	
	Block a user