def get_public_key(key_id):
    '''
        Takes the "keyId" field of a request and
        uses resolve_ap_object-actor to fetch the public key
        of the specified actor.
    '''
    actor = resolve_ap_object(key_id)

    if not actor or not actor.get('publicKey'):
        return error(
            'An error occurred while attempting to fetch the public key of the inbound actor.',
            400)

    public_key_wrapper = actor.get('publicKey')

    if public_key_wrapper.get('id') != key_id:
        return error('Keys don\'t match', 400)

    public_key_string = public_key_wrapper.get('publicKeyPem')

    if not public_key_string:
        return error('Public key not found.')

    public_key = RSA.importKey(bytes(public_key_string, 'utf-8'))

    return public_key
示例#2
0
def route_signup():
    json = request.get_json()
    username = str(json.get('username')).lower()
    password = str(json.get('password'))
    password_confirm = str(json.get('passwordConfirm'))
    actor_name = str(json.get('actorName'))

    existing_user = db.session.query(User).filter(
        db.func.lower(User.username) == db.func.lower(username)).first()
    if existing_user != None:
        return error('That username is not available.', 400)

    existing_actor = db.session.query(Actor).filter(
        db.func.lower(Actor.username) == db.func.lower(actor_name)).first()
    if existing_actor != None:
        return error('That actor name is not available.', 400)

    if password != password_confirm:
        return error('Passwords don\'t match.', 400)

    new_user = User(username, password)
    db.session.add(new_user)
    db.session.flush()

    new_actor = Actor(user_id=new_user.id, username=actor_name)
    db.session.add(new_actor)

    db.session.commit()

    session['uid'] = new_user.id

    return make_response('', 201)
示例#3
0
def handle_undo(inbound_json, actor):
    '''
        The Undo activity is one of a handful with side effects.
        These include deleting Like objects and undoing leader-follower
        relationships. These side effects are handled here.

        Returns: None
    '''
    undone_activity = None
    if isinstance(inbound_json['object'], str):
        undone_activity = APObject.get_object_from_url(inbound_json['object'])
    elif isinstance(inbound_json['object'], dict):
        undone_activity = APObject.get_object_from_url(
            inbound_json['object']['id'])

    if undone_activity is None:
        return error('Could not undo activity: activity not found.', 404)

    if undone_activity.internal_actor_id != actor.id:
        return error('You cannot undo activities performed by other actors.')

    if isinstance(undone_activity, Follow):
        pass
    elif isinstance(undone_activity, Like):
        db.session.delete(undone_activity)
    else:
        return error('You cannot undo that kind of activity.')
示例#4
0
def route_signin():
    json = request.get_json()
    username = str(json.get('username'))
    password = str(json.get('password'))

    if 'uid' in session:
        return error(
            'You are currently signed in. Please sign out before trying to sign in.'
        )

    err_msg = 'Invalid username or password.'

    user = db.session.query(User).filter(
        db.func.lower(User.username) == db.func.lower(username)).first()

    if user == None:
        return error(err_msg)

    if not bcrypt.checkpw(bytes(password, 'utf-8'),
                          bytes(user.password_hash, 'utf-8')):
        return error(err_msg)

    session['uid'] = user.id

    return make_response('', 200)
示例#5
0
def handle_inbound_follow(activity, obj):
    '''
        Handles the side effects of an incoming Follow activity.
        Returns: Flask response if an error occurs, None otherwise
    '''

    api_url = os.environ['API_URL']

    if obj['id'].find(f'{api_url}/actors/') < 0:
        return error('Invalid actor ID')

    local_actor_name = obj['id'].replace(f'{api_url}/actors/', '').lower()

    leader = db.session.query(Actor).filter(
        db.func.lower(Actor.username) == local_actor_name).first()

    if leader is None:
        return error('Actor not found', 404)

    follower = resolve_ap_object(activity['actor'])

    follower_shared_inbox = None
    follower_inbox = follower['inbox']

    if 'endpoints' in follower:
        if 'sharedInbox' in follower['endpoints']:
            follower_shared_inbox = follower['endpoints']['sharedInbox']

    accept_activity = Accept()
    accept_activity.set_actor(leader)
    accept_activity.set_object(activity)

    db.session.add(accept_activity)
    db.session.flush()

    message_body = accept_activity.to_dict()
    message_body['object'] = activity

    try:
        signed_request(leader, message_body, url=follower_inbox)
    except:
        return error('An error occurred while processing that follow request.',
                     400)

    new_followed_by = FollowedBy(leader.id, follower['id'], follower_inbox,
                                 follower_shared_inbox)
    db.session.add(new_followed_by)

    follower_username = follower['id']
    if 'preferredUsername' in follower:
        follower_username = follower['preferredUsername']

    db.session.add(
        Notification(leader, f'{follower_username} has followed you',
                     'Follow'))

    return None
示例#6
0
def handle_follow(inbound_json, actor, base_activity, base_object, is_local):
    '''
        Handles the various oddities associated with the correspondance nature of the Follow activity.
    '''
    leader = resolve_ap_object(inbound_json['object'])

    existing_follow = db.session.query(Following).filter(
        db.and_(Following.follower_id == actor.id,
                Following.leader == leader['id'])).first()

    if existing_follow is not None and existing_follow.approved is True:
        return error('You are already following this actor.')

    new_follow = Following(actor.id,
                           leader['id'],
                           leader['followers'],
                           approved=is_local)
    db.session.add(new_follow)

    # Due to the correspondance nature of the Follow activity, it has some very unusual requirements.
    # We need to detect if the incoming Follow activity is targeting a local user. If it is, we don't need
    # To deliver server-to-server messages about this transaction. Attempting to do so would cause problems
    # resulting from uncomitted database transactions and be a waste of resources.
    if is_local:
        local_leader = db.session.query(Actor).filter(
            db.func.lower(Actor.username) == db.func.lower(
                inbound_json['object'].replace(
                    f'{os.environ["API_URL"]}/actors/', ''))).first()
        if local_leader is None:
            return make_response('Actor not found', 404)
        actor_dict = actor.to_dict()
        new_followed_by = FollowedBy(
            local_leader.id,
            actor_dict['id'],
            actor_dict['inbox'],
            follower_shared_inbox=actor_dict['endpoints']['sharedInbox'],
            approved=True)
        db.session.add(
            Notification(
                local_leader,
                f'{actor_dict["preferredUsername"]} has followed you.',
                'Follow'))
        db.session.add(new_followed_by)
        db.session.commit()
    else:
        db.session.commit(
        )  # This is required so when we get an Accept activity back before the end of this request, we're able to find the Follow activity
        try:
            signed_request(actor, base_activity.to_dict(), leader['inbox'])
        except:
            return error(
                'Your follow request was not able to be delivered to that server.'
            )

    return make_response('', 200)
示例#7
0
def get_base_objects(obj):
    '''
        The client is capable of sending a wide array of object types to
        the server. This function parses the input AP Activity+Object pair and
        returns a tuple containing the correct database model(s).

        base_object is either a string or a database model depending on context

        Returns: Flask.response | tuple(base_activity, base_object)
    '''
    base_activity = None
    base_object = None

    err_not_supported = 'Vagabond does not currently support this type of AcvtivityPub object.'

    if obj['type'] == 'Create':
        if obj['object']['type'] == 'Note':
            base_object = Note()
            base_activity = Create()
        else:
            return error(err_not_supported)

    elif obj['type'] == 'Follow':
        base_activity = Follow()
    elif obj['type'] == 'Like':
        base_activity = Like()
    elif obj['type'] == 'Undo':
        base_activity = Undo()
    elif obj['type'] == 'Delete':
        base_activity = Delete()
    else:
        return error(err_not_supported)

    if base_object is None:
        if (isinstance(obj['object'], str)):
            base_object = obj['object']
        elif (isinstance(obj['object'], dict)):
            base_object = obj['object']['id']
        else:
            raise Exception(
                'Invalid Activity: object field was neither an object nor a string.'
            )

    db.session.add(base_activity)
    if isinstance(base_object, db.Model):
        db.session.add(base_object)
    db.session.flush()

    return (base_activity, base_object)
示例#8
0
def route_switch_actor(user):

    actor = db.session.query(Actor).filter(
        db.func.lower(Actor.username) == db.func.lower(user.username)).first()
    if actor is None:
        return error('Actor not found', 404)

    if actor.user_id == user.id:
        user.primary_actor_id = actor.id
        db.session.commit()

        return make_response('', 200)

    else:
        return error('User does not own actor.', 404)
示例#9
0
def route_get_actor_following_page(username, page):
    actor = db.session.query(Actor).filter(
        db.func.lower(Actor.username) == db.func.lower(username)).first()
    if actor is None:
        return error('Actor not found', 404)

    items = db.session.query(Following).filter(
        db.and_(Following.follower_id == actor.id,
                Following.approved == 1)).paginate(page, 20).items

    ordered_items = []

    for item in items:
        ordered_items.append(item.leader)

    return make_response(
        {
            '@context': 'https://www.w3.org/ns/activitystreams',
            'id':
            f'{config["api_url"]}/actors/{actor.username}/following/{page}',
            'type': 'OrderedCollectionPage',
            'totalItems': len(items),
            'partOf':
            f'{config["api_url"]}/actors/{actor.username}/following/',
            'orderedItems': ordered_items
        }, 200)
示例#10
0
def route_get_actor_following(username):
    '''
       Publicly accessible following collection. 
    '''

    actor = db.session.query(Actor).filter(
        db.func.lower(Actor.username) == db.func.lower(username)).first()
    if actor is None:
        return error('Actor not found', 404)

    items_per_page = 20
    total_items = db.session.query(Following).filter(
        db.and_(Following.follower_id == actor.id,
                Following.approved == 1)).count()
    last_page = ceil(total_items / items_per_page)

    return make_response(
        {
            '@context':
            'https://www.w3.org/ns/activitystreams',
            'id':
            f'{config["api_url"]}/actors/{actor.username}/following',
            'type':
            'OrderedCollection',
            'totalItems':
            total_items,
            'first':
            f'{config["api_url"]}/actors/{actor.username}/following/1',
            'last':
            f'{config["api_url"]}/actors/{actor.username}/following/{last_page}'
        }, 200)
示例#11
0
def handle_inbound_accept_reject(activity, obj):
    '''
        Incoming Accept and Reject activites on Follow objects
        have side effects which are handled here
    '''

    actor = APObject.get_object_from_url(
        obj['actor'])  #actor on local server who is following external actor
    if not isinstance(actor, Actor):
        raise Exception(
            'Remote server tried to accept or reject a Follow request done by an object and not by an actor. Aborting.'
        )

    following = db.session.query(Following).filter(
        db.and_(Following.follower_id == actor.id,
                Following.leader == activity['actor']),
        Following.approved == 0).first()

    follow_activity = db.session.query(Follow).filter(
        db.and_(Follow.external_object_id == obj['object'],
                Follow.internal_actor_id == actor.id)).first()

    if following is None or follow_activity is None:
        return error('Follow request not found.', 404)

    if activity['type'] == 'Accept':
        following.approved = True
        db.session.add(following)
    else:
        db.session.delete(following)

    return None
示例#12
0
def get_outbox(username):

    username = username.lower()

    actor = db.session.query(Actor).filter_by(username=username).first()
    if not actor:
        return error('Actor not found', 404)

    items_per_page = 20
    total_items = db.session.query(Activity).filter(
        Activity.internal_actor_id == actor.id).count()

    max_id_object = db.session.query(Activity).filter(
        Activity.internal_actor_id == actor.id).order_by(
            Activity.id.desc()).first()
    max_id = 0
    if max_id_object is not None:
        max_id = max_id_object.id

    max_page = ceil(total_items / items_per_page)
    api_url = os.environ['API_URL']

    output = {
        '@context': 'https://www.w3.org/ns/activitystreams',
        'id': f'{api_url}/actors/{username}/outbox',
        'type': 'OrderedCollection',
        'totalItems': total_items,
        'first': f'{api_url}/actors/{username}/outbox/1?maxId={max_id}',
        'last': f'{api_url}/actors/{username}/outbox/{max_page}?maxId={max_id}'
    }

    response = make_response(output, 200)
    response.headers['Content-Type'] = 'application/activity+json'
    return response
示例#13
0
def get_outbox(username):

    username = username.lower()

    actor = db.session.query(Actor).filter_by(username=username).first()
    if not actor:
        return error('Actor not found', 404)

    items_per_page = 20
    total_items = db.session.query(APObject).filter_by(
        type=APObjectType.NOTE).count()
    max_page = ceil(total_items / items_per_page)
    api_url = config['api_url']

    output = {
        '@context': 'https://www.w3.org/ns/activitystreams',
        'id': f'{api_url}/actors/{username}/outbox',
        'type': 'OrderedCollection',
        'totalItems': total_items,
        'first': f'{api_url}/actors/{username}/outbox/1',
        'last': f'{api_url}/actors/{username}/outbox/{max_page}'
    }

    response = make_response(output, 200)
    response.headers['Content-Type'] = 'application/activity+json'
    return response
示例#14
0
def route_get_actor_following_page(username, page):
    actor = db.session.query(Actor).filter(
        db.func.lower(Actor.username) == db.func.lower(username)).first()
    if actor is None:
        return error('Actor not found', 404)

    items = db.session.query(Following).filter(
        db.and_(Following.follower_id == actor.id,
                Following.approved == 1)).paginate(page, 20).items

    ordered_items = []

    for item in items:
        ordered_items.append(item.leader)

    api_url = os.environ['API_URL']

    output = {
        '@context': 'https://www.w3.org/ns/activitystreams',
        'id': f'{api_url}/actors/{actor.username}/following/{page}',
        'type': 'OrderedCollectionPage',
        'partOf': f'{api_url}/actors/{actor.username}/following',
        'next': f'{api_url}/actors/{actor.username}/following/{page+1}',
        'orderedItems': ordered_items
    }

    if page > 1:
        output[
            'prev'] = f'{api_url}/actors/{actor.username}/following/{page-1}'

    return make_response(output, 200)
示例#15
0
文件: inbox.py 项目: gerakey/Vagabond
def modify_follow(actor, activity, obj):
    '''
        Incoming Accept and Reject activites on Follow objects
        have side effects which are handled here
    '''

    following = db.session.query(Following).filter(
        db.and_(Following.follower_id == actor.id,
                Following.leader == activity['actor']),
        Following.approved == 0).first()

    follow_activity = db.session.query(Follow).filter(
        db.and_(Follow.external_object_id == obj['object'],
                Follow.internal_actor_id == actor.id)).first()

    if following is None or follow_activity is None:
        return error('Follow request not found.', 404)

    if activity['type'] == 'Accept':
        following.approved = True
        db.session.add(following)
    else:
        db.session.delete(following)

    db.session.delete(follow_activity)

    db.session.commit()

    return make_response('', 200)
示例#16
0
def route_user_outbox_paginated(actor_name, page):

    actor = db.session.query(Actor).filter_by(
        username=actor_name.lower()).first()

    if actor is None:
        return error('Actor not found', 404)

    activities = db.session.query(Activity).filter(
        db.and_(Activity.actor == actor,
                Activity.type != APObjectType.FOLLOW)).order_by(
                    Activity.published.desc()).paginate(page, 20).items
    api_url = config['api_url']

    orderedItems = []

    for activity in activities:
        orderedItems.append(activity.to_dict())

    output = {
        '@context': 'https://www.w3.org/ns/activitystreams',
        'id': f'{api_url}/actors/{actor_name}/outbox/{page}',
        'partOf': f'{api_url}/actors/{actor_name}/outbox',
        'type': 'OrderedCollectionPage',
        'prev': f'{api_url}/actors/{actor_name}/outbox/{page-1}',
        'next': f'{api_url}/actors/{actor_name}/outbox/{page+1}',
        'orderedItems': orderedItems
    }

    response = make_response(output, 200)
    response.headers['Content-Type'] = 'application/activity+json'
    return response
示例#17
0
def route_get_liked_collection(actor_name):

    actor = db.session.query(Actor).filter(
        db.func.lower(actor_name) == db.func.lower(Actor.username)).first()
    if actor is None:
        return error('Actor not found.', 404)

    max_id = 0
    max_id_object = db.session.query(Like).filter(
        Like.internal_actor_id == actor.id).order_by(Like.id.desc()).first()
    if max_id_object is not None:
        max_id = max_id_object.id

    total_items = db.session.query(Like).filter(
        Like.internal_actor_id == actor.id).count()
    items_per_page = 20

    max_page = ceil(total_items / items_per_page)

    api_url = os.environ['API_URL']

    output = {
        '@context': 'https://www.w3.org/ns/activitystreams',
        'id': f'{api_url}/actors/{actor_name}/liked',
        'type': 'OrderedCollection',
        'totalItems': total_items,
        'first': f'{api_url}/actors/{actor_name}/liked/1?maxId={max_id}',
        'last':
        f'{api_url}/actors/{actor_name}/liked/{max_page}?maxId={max_id}'
    }

    output = make_response(output, 200)
    output.headers['content-type'] = 'application/activity+json'
    return output
示例#18
0
def route_get_actor_followers(username):
    '''
       Publicly accessible followers collection. 
    '''

    actor = db.session.query(Actor).filter(
        db.func.lower(Actor.username) == db.func.lower(username)).first()
    if actor is None:
        return error('Actor not found', 404)

    items_per_page = 20
    total_items = db.session.query(FollowedBy).filter(
        FollowedBy.leader_id == actor.id).count()
    last_page = ceil(total_items / items_per_page)

    return make_response(
        {
            '@context':
            'https://www.w3.org/ns/activitystreams',
            'id':
            f'{os.environ["API_URL"]}/actors/{actor.username}/followers',
            'type':
            'OrderedCollection',
            'totalItems':
            total_items,
            'first':
            f'{os.environ["API_URL"]}/actors/{actor.username}/followers/1',
            'last':
            f'{os.environ["API_URL"]}/actors/{actor.username}/followers/{last_page}'
        }, 200)
示例#19
0
def handle_delete(inbound_json, actor):

    deleted_object = None
    if isinstance(inbound_json['object'], str):
        deleted_object = APObject.get_object_from_url(inbound_json['object'])
    elif isinstance(inbound_json['object'], dict):
        deleted_object = APObject.get_object_from_url(
            inbound_json['object']['id'])

    if deleted_object is None:
        return error('Could not delete activity: activity not found.', 404)

    if deleted_object.internal_author_id != actor.id:
        return error('You cannot delete objects created by other actors.')

    if isinstance(deleted_object, Note):
        db.session.delete(deleted_object)
示例#20
0
def route_get_object_by_id(object_id):

    ap_object = db.session.query(APObject).get(object_id)

    if ap_object is None:
        return error('Object not found', 404)

    response = make_response(ap_object.to_dict(), 200)
    response.headers['Content-Type'] = 'application/activity+json'
    return response
示例#21
0
def get_note_by_id(object_id):

    ap_object = db.session.query(APObject).filter(
        db.and_(APObject.id == object_id, APObject.external_id == None)).first()

    if ap_object is None:
        return error('Object not found', 404)
    response = make_response(ap_object.to_dict(), 200)
    response.headers['Content-Type'] = 'application/activity+json'
    return response
示例#22
0
def route_get_actor_by_username(username):
    '''
        Returns: ActivityPub actor object
    '''
    actor = db.session.query(Actor).filter(
        db.func.lower(Actor.username) == db.func.lower(username)).first()
    if actor is None:
        return error('Actor not found', 404)
    response = make_response(actor.to_dict(), 200)
    response.headers['Content-Type'] = 'application/activity+json'
    return response
示例#23
0
def route_get_liked_collection_paginated(actor_name, page):

    #actor check
    actor = db.session.query(Actor).filter(
        db.func.lower(actor_name) == db.func.lower(Actor.username)).first()
    if actor is None:
        return error('Actor not found.', 404)

    # set variables
    items_per_page = 20
    max_id = None
    if 'maxId' in request.args:
        max_id = int(request.args['maxId'])
    total_items = 0

    base_query = db.session.query(Like).filter(
        Like.internal_actor_id == actor.id).order_by(Like.id.desc())
    total_items = base_query.count()

    if max_id is not None:
        base_query = base_query.filter(Like.id <= max_id)

    api_url = os.environ['API_URL']

    output = {
        '@context': 'https://www.w3.org/ns/activitystreams',
        'id': f'{api_url}/actors/{actor_name}/liked/{page}',
        'type': 'OrderedCollectionPage',
        'partOf': f'{api_url}/actors/{actor.username}/liked',
        'orderedItems': []
    }

    for item in base_query.paginate(page, items_per_page).items:
        output['orderedItems'].append(item.to_dict())

    if max_id is not None:
        output['id'] = output['id'] + f'?maxId={max_id}'

    # previous page
    if page > 1:
        output['prev'] = f'{api_url}/actors/{actor_name}/liked/{page-1}'
        if max_id is not None:
            output['prev'] = output['prev'] + f'?maxId={max_id}'

    # optional next page
    if total_items > items_per_page * page:
        output['next'] = f'{api_url}/actors/{actor_name}/liked/{page}'
        if max_id is not None:
            output['next'] = output['next'] + f'?maxId={max_id}'

    output = make_response(output, 200)
    output.headers['content-type'] = 'application/activity+json'
    return output
示例#24
0
def route_user_outbox(actor_name):
    '''
        Post requests to an actor's outbox can come from either a C2S or S2S
        interaction. Here we determine which type of request is being received
        and act accordingly. GET requests are also permitted.
    '''
    if request.method == 'GET':
        return get_outbox(actor_name)
    elif request.method == 'POST' and 'uid' in session:
        return post_outbox_c2s(actor_name)
    else:
        return error('Invalid request')
示例#25
0
文件: inbox.py 项目: gerakey/Vagabond
def route_actor_inbox_paginated(user, actor_name, page):

    has_permission = False
    actor = None
    for _actor in user.actors:
        if _actor.username.lower() == actor_name.lower():
            has_permission = True
            actor = _actor
            break

    if not has_permission:
        return error('You can only view your own inbox, not someone else\'s!')

    return get_inbox_paginated(actor, page, personalized=True)
示例#26
0
def handle_undo(inbound_json, activity, obj):
    print(json.dumps(inbound_json))
    if obj['type'] == 'Follow':

        leader = APObject.get_object_from_url(obj['object'])
        if leader is None or not isinstance(leader, Actor):
            return error('Actor not found.', 404)

        follow_activity = db.session.query(Follow).filter(
            Follow.external_id == obj['id']).first()
        if follow_activity is None:
            return error('Cannot undo follow activity: follow not found.', 404)

        followed_by = db.session.query(FollowedBy).filter(
            FollowedBy.follower == inbound_json['actor']).filter(
                FollowedBy.leader_id == leader.id).first()
        if followed_by is None:
            return error('Cannot undo follow activity: follow not found.', 404)

        db.session.delete(follow_activity)
        db.session.delete(followed_by)
        db.session.commit()

        return make_response('', 200)
示例#27
0
def post_outbox_c2s(actor_name, *args, **kwargs):

    user = kwargs['user']
    is_own_outbox = False
    actor = None
    for _actor in user.actors:
        if _actor.username.lower() == actor_name.lower():
            is_own_outbox = True
            actor = _actor
            break

    if not is_own_outbox:
        return error(
            'You can\'t post to the outbox of an actor that isn\'t yours.')

    _type = request.get_json().get('type')
    if _type is None:
        return error('Invalid ActivityPub object type')
    elif _type == 'Note':
        return create_note(actor, request.get_json())
    elif _type == 'Follow':
        return follow(actor, request.get_json())

    return error('Invalid ActivityPub object type.')
示例#28
0
def follow(actor, follow_activity):
    '''
        actor: Actor model
        follow_activity: Dictionary representation of the new follow request
    '''

    leader = resolve_ap_object(follow_activity['object'])

    existing_follow = db.session.query(Following).filter(
        db.and_(Following.follower_id == actor.id,
                Following.leader == leader['id'])).first()

    if existing_follow is not None:
        if existing_follow.approved is True:
            return error('You are already following this actor.')

        db.session.delete(existing_follow)

    new_activity = Follow()
    new_activity.set_actor(actor)
    new_activity.set_object(follow_activity['object'])
    db.session.add(new_activity)
    db.session.flush()

    new_follow = Following(actor.id, leader['id'], leader['followers'])
    db.session.add(new_follow)

    response = signed_request(actor, new_activity.to_dict(), leader['inbox'])

    db.session.commit()

    if response.status_code >= 400:

        return error('Something went wrong :(')

    return make_response('', 200)
示例#29
0
文件: inbox.py 项目: gerakey/Vagabond
def get_actor_inbox(actor_name, user=None):

    actor_name = actor_name.lower()

    has_permission = False
    for _actor in user.actors:
        if _actor.username.lower() == actor_name:
            has_permission = True
            break

    if has_permission:
        return get_inbox(personalized=True)
    else:
        #TODO: Non-watseful way of figuring out which notes go to who
        return error('You can only view your own inbox, not someone else\'s!')
示例#30
0
def route_add_new_actor(user):
    actor_name = request.get_json().get('actorName')

    existing_actor = db.session.query(Actor).filter(
        db.func.lower(Actor.username) == db.func.lower(actor_name)).first()
    if existing_actor is not None:
        return error('That actor name is not available.', 404)

    new_actor = Actor(actor_name, user_id=user.id)
    db.session.add(new_actor)
    db.session.flush()

    db.session.commit()

    return make_response('', 201)