diff --git a/.gitignore b/.gitignore index 1451eee257de9b65c50c80c1ee1ac435efcea20d..1859bb4b1ad485cf6dedcc39057e156443a1b9b5 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ *.swp venv instance/* +api.db diff --git a/api/app.py b/api/app.py index 1da83ce8e08daa5b674459b00b6ab984c57aca03..a5031847df3b5d83e38729e8ce2ae884aa841083 100644 --- a/api/app.py +++ b/api/app.py @@ -1,9 +1,9 @@ import json -from flask import Flask, redirect, render_template, url_for -from decorators import admin -from models import db -from strava import Strava +from flask import Flask, redirect, render_template, request, url_for from stravalib import unithelper +from api.decorators import admin +from api.models import db, ContactFormSubmission +from api.strava import Strava app = Flask(__name__, instance_relative_config=True) app.config.from_object('config') @@ -23,23 +23,32 @@ def strava_auth(): else: return Strava.authorize() -@app.route('/api/<api_name>') -def api(api_name): - if api_name == "strava": - s = Strava() - return json.dumps([{ - 'name': a.name, - 'type': a.type, - 'moving_time': a.moving_time.seconds, - 'date': str(a.start_date), - 'distance': unithelper.miles(a.distance).num, - 'athlete_id': a.athlete.id, - 'url': 'https://strava.com/activity/{}'.format(a.id) - } for a in s.activities]) +@app.route('/api/strava') +def api_strava(): + s = Strava() + r = app.make_response(json.dumps([{ + 'name': a.name, + 'type': a.type, + 'moving_time': a.moving_time.seconds, + 'date': str(a.start_date), + 'distance': unithelper.miles(a.distance).num, + 'athlete_id': a.athlete.id, + 'url': 'https://strava.com/activity/{}'.format(a.id) + } for a in s.activities])) + r.mimetype = 'application/json' + return r + +@app.route('/api/contact', methods=['POST']) +def api_contact(): + json = request.json + db.session.add(ContactFormSubmission(email=json['email'], + name=json['name'], text=json['text'])) + db.session.commit() + return 'ok' @app.context_processor def cp_is_admin(): - from util import is_admin + from api.util import is_admin return dict(is_admin=is_admin) @app.context_processor diff --git a/api/contact.py b/api/contact.py new file mode 100644 index 0000000000000000000000000000000000000000..2ad00607c0afc8851cdc1f7c35d56495c8826e49 --- /dev/null +++ b/api/contact.py @@ -0,0 +1,21 @@ +from flask import request, redirect, current_app +from stravalib.client import Client +from requests.exceptions import HTTPError +from sqlalchemy.orm.exc import NoResultFound +from api.models import db, StravaApiToken + +class Contact(): + """Website contact form handler""" + + @classmethod + def check_auth(cls, token = None): + if not token: + try: + token = db.session.query(StravaApiToken).one().token + except NoResultFound: + return False + strava = Client(token) + try: + return strava.get_athlete().email + except (HTTPError, AttributeError): + return False diff --git a/api/decorators.py b/api/decorators.py index 51947d007f2ace9407de39bc9d8c60be403bd710..e0ce6aa8851e51402a6a9d8a23cc2dfaae20adbe 100644 --- a/api/decorators.py +++ b/api/decorators.py @@ -1,6 +1,6 @@ from functools import wraps from flask import abort -from util import is_admin +from api.util import is_admin def admin(f): @wraps(f) @@ -8,4 +8,4 @@ def admin(f): if is_admin(): return abort(403) return f(*args, **kwargs) - + diff --git a/api/models.py b/api/models.py index 2bf2c79ba7b815a20f2586d6cb5892d98c5d846e..7922f806176d69f3e59caf4f02cf7d436a1b558d 100644 --- a/api/models.py +++ b/api/models.py @@ -1,4 +1,8 @@ +import datetime +import smtplib +from email.mime.text import MIMEText from flask_sqlalchemy import SQLAlchemy +from flask import current_app db = SQLAlchemy() @@ -7,3 +11,25 @@ class StravaApiToken(db.Model): __tablename__ = "strava_api_token" id = db.Column(db.Integer, primary_key=True) token = db.Column(db.String) + +class ContactFormSubmission(db.Model): + """Submission via the contact form""" + __tablename__ = "contact_form_submission" + id = db.Column(db.Integer, primary_key=True) + date = db.Column(db.DateTime) + name = db.Column(db.String) + email = db.Column(db.String) + text = db.Column(db.Text) + + + def __init__(self, **kwargs): + kwargs['date'] = datetime.datetime.now() + super(ContactFormSubmission, self).__init__(**kwargs) + footer = "\n\nSubmitted via website contact form." + msg = MIMEText("{body}\n\n{footer}".format(body=self.text, footer=footer)) + msg['Subject'] = 'Website Contact Form Submission' + msg['From'] = '{name} <{email}>'.format(name=self.name, email=self.email) + msg['To'] = current_app.config.get('ADMIN_EMAIL') + s = smtplib.SMTP(current_app.config.get('SMTP_HOST', 'localhost')) + s.send_message(msg) + s.quit() diff --git a/api/strava.py b/api/strava.py index 04b85dfeb2f3684377480788919d9fb8b37a9e15..2fb908d76d69086729245aa2f042ddadca034a3a 100644 --- a/api/strava.py +++ b/api/strava.py @@ -2,7 +2,7 @@ from flask import request, redirect, current_app from stravalib.client import Client from requests.exceptions import HTTPError from sqlalchemy.orm.exc import NoResultFound -from models import db, StravaApiToken +from api.models import db, StravaApiToken class Strava(Client): """Wrapper for stavalib Client, which is a Strava API client. diff --git a/api/templates/index.html b/api/templates/index.html index 276b125ea1d446545b0137a5e32490741826c2e0..58d418feef97db095a5d3844d2f500171fba1c6b 100644 --- a/api/templates/index.html +++ b/api/templates/index.html @@ -17,7 +17,7 @@ </ul> </nav> {% else %} - <header><h1>Hi</h1></header> - <p>There is nothing here for you.</p> + <header><h1>Oops</h1></header> + <p>This is not my website. My website is <a href="https://ant.sr">here</a>.</p> {% endif %} {% endblock %} diff --git a/api/util.py b/api/util.py index bdbbcc47ed43dd0f25ba2b17b595b6e48fcf552e..03fbc8b2cd5b97e7c21a3e45ce94d0a74db48720 100644 --- a/api/util.py +++ b/api/util.py @@ -1,8 +1,5 @@ from flask import request, current_app def is_admin(): - try: - admin_ip = current_app.config.ADMIN_IP - except AttributeError: - admin_ip = '127.0.0.1' + admin_ip = current_app.config.get('ADMIN_IP', '127.0.0.1') return request.remote_addr == admin_ip diff --git a/config.py b/config.py index ca7d9db1aeced7e8c99c3ef8cab4f4c2984a15fc..ca590d0f3f90a9b77bee8c1218e4a297f2faa75e 100644 --- a/config.py +++ b/config.py @@ -1,6 +1,7 @@ STRAVA_APP_ID = '12345' STRAVA_APP_SECRET = 'your-strava-app-secret' ADMIN_IP = '127.0.0.1' +ADMIN_EMAIL = 'mail@ant.sr' SQLALCHEMY_DATABASE_URI = 'sqlite:///api.db' SQLALCHEMY_TRACK_MODIFICATIONS = True diff --git a/migrations/versions/f640a1d27e31_.py b/migrations/versions/f640a1d27e31_.py new file mode 100644 index 0000000000000000000000000000000000000000..6e0698408e8c00e0e9e24876bc45dab39b5b3bef --- /dev/null +++ b/migrations/versions/f640a1d27e31_.py @@ -0,0 +1,33 @@ +"""empty message + +Revision ID: f640a1d27e31 +Revises: 28df97a5e20f +Create Date: 2016-10-13 00:51:38.915838 + +""" + +# revision identifiers, used by Alembic. +revision = 'f640a1d27e31' +down_revision = '28df97a5e20f' + +from alembic import op +import sqlalchemy as sa + + +def upgrade(): + ### commands auto generated by Alembic - please adjust! ### + op.create_table('contact_form_submission', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('date', sa.DateTime(), nullable=True), + sa.Column('name', sa.String(), nullable=True), + sa.Column('email', sa.String(), nullable=True), + sa.Column('text', sa.Text(), nullable=True), + sa.PrimaryKeyConstraint('id') + ) + ### end Alembic commands ### + + +def downgrade(): + ### commands auto generated by Alembic - please adjust! ### + op.drop_table('contact_form_submission') + ### end Alembic commands ### diff --git a/requirements.txt b/requirements.txt index aa10f9bdb7f5f7a96c2ef199a5eff972c2a0ead8..3af8b966316254a14ce9d9102002a0bb0a836dc4 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,4 +3,4 @@ Flask-Migrate==1.8.1 Flask-SQLAlchemy==2.1 psycopg2==2.6.2 SQLAlchemy==1.0.14 -stravalib==0.5.0 +stravalib==0.6.3