raphael
596f690cce
- the color picker is from 'colorPick' which makes implementation easy. It's only a .js and a .css file which need to be included and the color-picker is simple enough to handle on the client side. The only main problem with it, is that accessing the 'id' information of the css it was called from is not straightforward - the toggle switch which is just implemented in .css currently has the checklist inverted, works but ugly - The Client sends this information back to the server via a json file, which defines the calendar id and either color or the toggle information. The server currently just prints this information.great stuff.
253 lines
7.9 KiB
Python
253 lines
7.9 KiB
Python
# Python standard libraries
|
|
import json
|
|
import os
|
|
import sqlite3
|
|
|
|
# Third-party libraries
|
|
import flask
|
|
from flask import Flask, redirect, request, url_for
|
|
from flask_login import (
|
|
LoginManager,
|
|
current_user,
|
|
login_required,
|
|
login_user,
|
|
logout_user,
|
|
)
|
|
from oauthlib.oauth2 import WebApplicationClient
|
|
import requests
|
|
|
|
# Internal imports
|
|
from database.db import init_db_command
|
|
from database.user import User
|
|
|
|
import caltojson
|
|
|
|
import google.oauth2.credentials
|
|
import google_auth_oauthlib.flow
|
|
import googleapiclient.discovery
|
|
|
|
# Configuration
|
|
|
|
CLIENT_SECRETS_FILE = "certificate/client_secret.json"
|
|
|
|
# This OAuth 2.0 access scope allows for full read/write access to the
|
|
# authenticated user's account and requires requests to use an SSL connection.
|
|
SCOPES = ["https://www.googleapis.com/auth/userinfo.email", "https://www.googleapis.com/auth/userinfo.profile", "https://www.googleapis.com/auth/calendar.readonly", "openid"]
|
|
API_SERVICE_NAME = 'calendar'
|
|
API_VERSION = 'v3'
|
|
|
|
GOOGLE_CLIENT_ID ="377787187748-shuvi4iq5bi4gdet6q3ioataimobs4lh.apps.googleusercontent.com"
|
|
GOOGLE_CLIENT_SECRET = "Hu_YWmKsVKUcLwyeINYzdKfZ"
|
|
GOOGLE_DISCOVERY_URL = (
|
|
"https://accounts.google.com/.well-known/openid-configuration"
|
|
)
|
|
# Flask app setup
|
|
app = Flask(__name__,
|
|
static_folder='static',
|
|
template_folder='template')
|
|
app.secret_key = os.environ.get("SECRET_KEY") or os.urandom(24)
|
|
|
|
# User session management setup
|
|
# https://flask-login.readthedocs.io/en/latest
|
|
login_manager = LoginManager()
|
|
login_manager.init_app(app)
|
|
|
|
# Naive database setup
|
|
try:
|
|
init_db_command()
|
|
except sqlite3.OperationalError:
|
|
# Assume it's already been created
|
|
pass
|
|
|
|
# OAuth 2 client setup
|
|
client = WebApplicationClient(GOOGLE_CLIENT_ID)
|
|
|
|
# Flask-Login helper to retrieve a user from our db
|
|
@login_manager.user_loader
|
|
def load_user(user_id):
|
|
return User.get(user_id)
|
|
|
|
@app.route("/")
|
|
def account():
|
|
return flask.redirect('account')
|
|
|
|
@app.route("/account")
|
|
def index():
|
|
if current_user.is_authenticated:
|
|
return (flask.render_template('account.html',
|
|
username = current_user.name, email = current_user.email, profile_img=current_user.profile_pic
|
|
)
|
|
)
|
|
else:
|
|
return flask.render_template('login.html')
|
|
|
|
def get_google_provider_cfg():
|
|
return requests.get(GOOGLE_DISCOVERY_URL).json()
|
|
|
|
class Calendar:
|
|
def __init__(self, name, color):
|
|
self.name = name
|
|
self.color = color
|
|
|
|
@app.route("/calendar")
|
|
@login_required
|
|
def calendar():
|
|
ca1 = Calendar("Hightower", "#30ff30")
|
|
ca2 = Calendar("Toast", "#66e230")
|
|
calendars = [ca1, ca2]
|
|
return flask.render_template('calendar.html', calendars=calendars)
|
|
|
|
|
|
@app.route('/test')
|
|
def test_api_request():
|
|
if 'credentials' not in flask.session:
|
|
return flask.redirect('login/google')
|
|
|
|
# Load credentials from the session.
|
|
credentials = google.oauth2.credentials.Credentials(
|
|
**flask.session['credentials'])
|
|
|
|
todaysCal = caltojson.generateJsonFromCalendarEntries(credentials)
|
|
|
|
|
|
# Save credentials back to session in case access token was refreshed.
|
|
# ACTION ITEM: In a production app, you likely want to save these
|
|
# credentials in a persistent database instead.
|
|
flask.session['credentials'] = credentials_to_dict(credentials)
|
|
|
|
with open('./userinfo/' + current_user.id + '/calendarevents.json', 'w') as outfile:
|
|
json.dump(todaysCal, outfile)
|
|
return flask.jsonify(todaysCal)
|
|
|
|
@app.route("/login/google")
|
|
def login():
|
|
|
|
'''
|
|
# Find out what URL to hit for Google login
|
|
google_provider_cfg = get_google_provider_cfg()
|
|
authorization_endpoint = google_provider_cfg["authorization_endpoint"]
|
|
|
|
# Use library to construct the request for Google login and provide
|
|
# scopes that let you retrieve user's profile from Google
|
|
request_uri = client.prepare_request_uri(
|
|
authorization_endpoint,
|
|
redirect_uri=request.base_url + "/callback",
|
|
scope=["openid", "email", "profile", "https://www.googleapis.com/auth/calendar.readonly"],
|
|
)
|
|
return redirect(request_uri)
|
|
'''
|
|
|
|
# Create flow instance to manage the OAuth 2.0 Authorization Grant Flow steps.
|
|
flow = google_auth_oauthlib.flow.Flow.from_client_secrets_file(
|
|
CLIENT_SECRETS_FILE, scopes=SCOPES)
|
|
# The URI created here must exactly match one of the authorized redirect URIs
|
|
# for the OAuth 2.0 client, which you configured in the API Console. If this
|
|
# value doesn't match an authorized URI, you will get a 'redirect_uri_mismatch'
|
|
# error.
|
|
flow.redirect_uri = request.base_url + "/callback"
|
|
authorization_url, state = flow.authorization_url(
|
|
# Enable offline access so that you can refresh an access token without
|
|
# re-prompting the user for permission. Recommended for web server apps.
|
|
access_type='offline',
|
|
# Enable incremental authorization. Recommended as a best practice.
|
|
include_granted_scopes='true')
|
|
|
|
# Store the state so the callback can verify the auth server response.
|
|
flask.session['state'] = state
|
|
|
|
return flask.redirect(authorization_url)
|
|
|
|
@app.route("/login/google/callback")
|
|
def callback():
|
|
# Specify the state when creating the flow in the callback so that it can
|
|
# verified in the authorization server response.
|
|
state = flask.session['state']
|
|
|
|
flow = google_auth_oauthlib.flow.Flow.from_client_secrets_file(
|
|
CLIENT_SECRETS_FILE, scopes=SCOPES, state=state)
|
|
flow.redirect_uri = request.base_url
|
|
|
|
# Use the authorization server's response to fetch the OAuth 2.0 tokens.
|
|
authorization_response = flask.request.url
|
|
flow.fetch_token(authorization_response=authorization_response)
|
|
|
|
# Store credentials in the session.
|
|
# ACTION ITEM: In a production app, you likely want to save these
|
|
# credentials in a persistent database instead.
|
|
credentials = flow.credentials
|
|
flask.session['credentials'] = credentials_to_dict(credentials)
|
|
|
|
session = flow.authorized_session()
|
|
|
|
userinfo = session.get('https://www.googleapis.com/userinfo/v2/me').json()
|
|
|
|
# Create a user in your db with the information provided
|
|
# by Google
|
|
user = User(
|
|
id_=userinfo['id'],
|
|
name=userinfo['name'],
|
|
email=userinfo['email'],
|
|
profile_pic=userinfo['picture']
|
|
)
|
|
|
|
# Doesn't exist? Add it to the database.
|
|
if not User.get(user.id):
|
|
User.create(user.id, user.name, user.email, user.profile_pic)
|
|
|
|
# Begin user session by logging the user in
|
|
login_user(user)
|
|
return flask.redirect(flask.url_for('index'))
|
|
|
|
@app.route("/logout")
|
|
@login_required
|
|
def logout():
|
|
logout_user()
|
|
return redirect(url_for("index"))
|
|
|
|
def credentials_to_dict(credentials):
|
|
return {'token': credentials.token,
|
|
'refresh_token': credentials.refresh_token,
|
|
'token_uri': credentials.token_uri,
|
|
'client_id': credentials.client_id,
|
|
'client_secret': credentials.client_secret,
|
|
'scopes': credentials.scopes}
|
|
|
|
|
|
@app.route("/userinfo/<path:user>/calendarevents.json")
|
|
def downloader(user):
|
|
print(user)
|
|
path = "/home/raphael/dev/website_ws/website/userinfo/" + user
|
|
return flask.send_from_directory(path, "calendarevents.json")
|
|
|
|
# POST
|
|
|
|
@app.route('/calendar', methods = ['POST', 'DELETE'])
|
|
@login_required
|
|
def user():
|
|
if request.method == 'POST':
|
|
calId = request.json.get('calendar_id')
|
|
color = request.json.get('color')
|
|
toggle = request.json.get('toggle')
|
|
|
|
print(calId)
|
|
if color != None:
|
|
print(color)
|
|
if toggle != None:
|
|
print(toggle)
|
|
# toggle specific calendar of user
|
|
elif request.method == 'DELETE':
|
|
# do nothing
|
|
return 'NONE'
|
|
else:
|
|
# POST Error 405
|
|
print("405")
|
|
|
|
return 'OK'
|
|
|
|
if __name__ == "__main__":
|
|
context = ('certificate/xip.io.crt', 'certificate/xip.io.key')#certificate and key files
|
|
app.run('0.0.0.0', 1234, ssl_context=context, debug=True)
|
|
|
|
|
|
|