Adding feed to public API
This commit is contained in:
parent
5536e764e7
commit
dc33b5567a
5 changed files with 113 additions and 8 deletions
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
## 0.7-dev
|
||||
|
||||
* Biggest change: unpacking modules. The single `app.py` file has become an `app` package, with submodules `models.py`, `utils.py`, `filters.py`, `website.py` and `ajax.py`.
|
||||
* Biggest change: unpacking modules. The single `app.py` file has become an `app` package, with submodules `models.py`, `utils.py`, `filters.py`, `website.py` and `ajax.py`. There is also a new module `api.py`.
|
||||
* Now `/about/` shows Python and Flask versions.
|
||||
* Now the error 404 handler returns HTTP 404.
|
||||
* Added user followers and following lists, accessible via `/+<username>/followers` and `/+<username>/following` and from the profile info box, linked to the followers/following number.
|
||||
|
|
@ -11,6 +11,7 @@
|
|||
* Added the capability to change password.
|
||||
* Corrected a bug into `pwdhash`: it accepted an argument, but pulled data from the form instead of processing it. Now it uses the argument.
|
||||
* Schema changes: added column `telegram` to `UserProfile` table. To update schema, execute the script `migrate_0_6_to_0_7.py`
|
||||
* Adding public API. Each of the API endpoints take a mandatory query string argument: the access token, generated by a separate endpoint at `/get_access_token` and stored into the client. All API routes start with `/api/V1`. Added endpoints `feed`.
|
||||
|
||||
## 0.6.0
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
A simple social network, inspired by the now dead Google-Plus.
|
||||
|
||||
To run the app, run the file "run_example.py"
|
||||
To run the app, do "flask run" in the package's parent directory.
|
||||
|
||||
Based on Tweepee example of [peewee](https://github.com/coleifer/peewee/).
|
||||
|
||||
|
|
@ -13,6 +13,7 @@ Based on Tweepee example of [peewee](https://github.com/coleifer/peewee/).
|
|||
* Timeline feed
|
||||
* Add info to your profile
|
||||
* In-site notifications
|
||||
* Public API
|
||||
* SQLite-based app
|
||||
|
||||
## Requirements
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@ from flask import (
|
|||
Flask, abort, flash, g, jsonify, redirect, render_template, request,
|
||||
send_from_directory, session, url_for, __version__ as flask_version)
|
||||
import hashlib
|
||||
from peewee import *
|
||||
import datetime, time, re, os, sys, string, json, html
|
||||
from functools import wraps
|
||||
from flask_login import LoginManager
|
||||
|
|
@ -76,12 +75,46 @@ def robots_txt():
|
|||
def uploads(id, type='jpg'):
|
||||
return send_from_directory(UPLOAD_DIRECTORY, id + '.' + type)
|
||||
|
||||
@app.route('/get_access_token', methods=['POST'])
|
||||
def send_access_token():
|
||||
try:
|
||||
try:
|
||||
user = User.get(
|
||||
(User.username == request.form['username']) &
|
||||
(User.password == pwdhash(request.form['password'])))
|
||||
except User.DoesNotExist:
|
||||
return jsonify({
|
||||
'message': 'Invalid username or password',
|
||||
'login_correct': False,
|
||||
'status': 'ok'
|
||||
})
|
||||
if user.is_disabled == 1:
|
||||
user.is_disabled = 0
|
||||
elif user.is_disabled == 2:
|
||||
return jsonify({
|
||||
'message': 'Your account has been disabled by violating our Terms.',
|
||||
'login_correct': False,
|
||||
'status': 'ok'
|
||||
})
|
||||
return jsonify({
|
||||
'token': generate_access_token(user),
|
||||
'login_correct': True,
|
||||
'status': 'ok'
|
||||
})
|
||||
except Exception:
|
||||
sys.excepthook(*sys.exc_info())
|
||||
return jsonify({
|
||||
'message': 'An unknown error has occurred.',
|
||||
'status': 'fail'
|
||||
})
|
||||
|
||||
from .website import bp
|
||||
app.register_blueprint(bp)
|
||||
|
||||
from .ajax import bp
|
||||
app.register_blueprint(bp)
|
||||
|
||||
|
||||
from .api import bp
|
||||
app.register_blueprint(bp)
|
||||
|
||||
|
||||
|
|
|
|||
67
app/api.py
Normal file
67
app/api.py
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
from flask import Blueprint, jsonify, request
|
||||
import sys, datetime
|
||||
from functools import wraps
|
||||
from .models import User, Message
|
||||
from .utils import check_access_token, Visibility
|
||||
|
||||
bp = Blueprint('api', __name__, url_prefix='/api/V1')
|
||||
|
||||
def get_message_info(message):
|
||||
return {
|
||||
'id': message.id,
|
||||
'user': {
|
||||
'id': message.user.id,
|
||||
'username': message.user.username,
|
||||
},
|
||||
'text': message.text,
|
||||
'privacy': message.privacy,
|
||||
'pub_date': message.pub_date.timestamp()
|
||||
}
|
||||
|
||||
def validate_access(func):
|
||||
@wraps(func)
|
||||
def wrapper(*args, **kwargs):
|
||||
access_token = request.args.get('access_token')
|
||||
if access_token is None:
|
||||
return jsonify({
|
||||
'message': 'missing access_token',
|
||||
'status': 'fail'
|
||||
})
|
||||
user = check_access_token(access_token)
|
||||
if user is None:
|
||||
return jsonify({
|
||||
'message': 'invalid access_token',
|
||||
'status': 'fail'
|
||||
})
|
||||
try:
|
||||
result = func(user, *args, **kwargs)
|
||||
assert isinstance(result, dict)
|
||||
except Exception:
|
||||
sys.excepthook(*sys.exc_info())
|
||||
return jsonify({
|
||||
'message': str(sys.exc_info()[1]),
|
||||
'status': 'fail'
|
||||
})
|
||||
result['status'] = 'ok'
|
||||
return jsonify(result)
|
||||
return wrapper
|
||||
|
||||
@bp.route('/feed')
|
||||
@validate_access
|
||||
def feed(self):
|
||||
timeline_media = []
|
||||
date = request.args.get('offset')
|
||||
if date is None:
|
||||
date = datetime.datetime.now()
|
||||
else:
|
||||
date = datetime.datetime.fromtimestamp(date)
|
||||
query = Visibility(Message
|
||||
.select()
|
||||
.where(((Message.user << self.following())
|
||||
| (Message.user == self))
|
||||
& (Message.pub_date < date))
|
||||
.order_by(Message.pub_date.desc())
|
||||
.limit(20))
|
||||
for message in query:
|
||||
timeline_media.append(get_message_info(message))
|
||||
return {'timeline_media': timeline_media}
|
||||
11
app/utils.py
11
app/utils.py
|
|
@ -178,13 +178,16 @@ def generate_access_token(user):
|
|||
h.update(str(user.password).encode('utf-8'))
|
||||
return str(user.id) + ':' + h.hexdigest()[:32]
|
||||
|
||||
def check_access_token(user, token):
|
||||
def check_access_token(token):
|
||||
uid, hh = token.split(':')
|
||||
if uid != user.get_id():
|
||||
return False
|
||||
try:
|
||||
user = User[uid]
|
||||
except User.DoesNotExist:
|
||||
return
|
||||
h = hashlib.sha256(get_secret_key())
|
||||
h.update(b':')
|
||||
h.update(str(user.id).encode('utf-8'))
|
||||
h.update(b':')
|
||||
h.update(str(user.password).encode('utf-8'))
|
||||
return h.hexdigest()[:32] == hh
|
||||
if h.hexdigest()[:32] == hh:
|
||||
return user
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue