Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 25 additions & 1 deletion app.yaml
Original file line number Diff line number Diff line change
@@ -1 +1,25 @@
runtime: python39
#Copyright 2021 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
runtime: python39

handlers:
# This configures Google App Engine to serve the files in the app's static
# directory.
- url: /static
static_dir: static
# This handler routes all requests not caught above to your main app. It is
# required when static routes are defined, but can be omitted (along with
# the entire handlers section) when there are no static files defined.
- url: /.*
script: auto
21 changes: 21 additions & 0 deletions index.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Copyright 2021 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

indexes:

- kind: visit
ancestor: yes
properties:
- name: timestamp
direction: desc
112 changes: 76 additions & 36 deletions main.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,71 @@
import logging
from flask import Flask, render_template, request, url_for
import datetime

from flask import Flask, render_template, request
from google.auth.transport import requests
from google.cloud import datastore
import google.oauth2.id_token

app = Flask(__name__)
@app.route('/')
firebase_request_adapter = requests.Request()

# [START gae_python38_datastore_store_and_fetch_user_times]
# [START gae_python3_datastore_store_and_fetch_user_times]
datastore_client = datastore.Client()

# [END gae_python3_datastore_store_and_fetch_user_times]
# [END gae_python38_datastore_store_and_fetch_user_times]
app = Flask(__name__)


# [START gae_python38_datastore_store_and_fetch_user_times]
# [START gae_python3_datastore_store_and_fetch_user_times]
def store_time(email, dt):
entity = datastore.Entity(key=datastore_client.key('User', email, 'visit'))
entity.update({
'timestamp': dt
})

datastore_client.put(entity)


def fetch_times(email, limit):
ancestor = datastore_client.key('User', email)
query = datastore_client.query(kind='visit', ancestor=ancestor)
query.order = ['-timestamp']

times = query.fetch(limit=limit)

return times


@app.route('/')
def root():
# Verify Firebase auth.
id_token = request.cookies.get("token")
error_message = None
claims = None
times = None

if id_token:
try:
# Verify the token against the Firebase Auth API. This example
# verifies the token on each page load. For improved performance,
# some applications may wish to cache results in an encrypted
# session store (see for instance
# http://flask.pocoo.org/docs/1.0/quickstart/#sessions).
claims = google.oauth2.id_token.verify_firebase_token(
id_token, firebase_request_adapter)

store_time(claims['email'], datetime.datetime.now())
times = fetch_times(claims['email'], 10)

except ValueError as exc:
# This will be raised if the token is expired or any other
# verification checks fail.
error_message = str(exc)

return render_template(
'index.html',
user_data=claims, error_message=error_message, times=times)

@app.route('/home')
def home():
Expand All @@ -21,35 +83,13 @@ def register():
def login():
return render_template('login.html')

# [END form]
# [START submitted]

@app.route('/submitted', methods=['POST'])
def submitted_form():
name = request.form['name']
email = request.form['email']
site = request.form['site_url']
comments = request.form['comments']
# [END submitted]
# [START render_template]
return render_template(
'submitted_form.html',
name=name,
email=email,
site=site,
comments=comments)
# [END render_template]

@app.errorhandler(500)
def server_error(e):
# Log the error and stacktrace.
logging.exception('An error occurred during a request.')
return 'An internal error occurred.', 500

@app.errorhandler(404)
def page_not_found(error):
return render_template('404.html'), 404

if __name__ == '__main__':
# Only run for local development.
app.run(host='127.0.0.1', port=8080, debug=True)
if __name__ == '__main__':
# This is used when running locally only. When deploying to Google App
# Engine, a webserver process such as Gunicorn will serve the app. This
# can be configured by adding an `entrypoint` to app.yaml.

# Flask's development server will automatically serve static files in
# the "static" directory. See:
# http://flask.pocoo.org/docs/1.0/quickstart/#static-files. Once deployed,
# App Engine itself will serve those files as configured in app.yaml.
app.run(host='127.0.0.1', port=8080, debug=True)
5 changes: 4 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1 +1,4 @@
Flask==2.1.0
Flask==2.0.1
google-cloud-datastore==2.7.1
google-auth==1.31.0
requests==2.26.0
72 changes: 72 additions & 0 deletions static/script.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/**
* Copyright 2018, Google LLC
* Licensed under the Apache License, Version 2.0 (the `License`);
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an `AS IS` BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

// [START gae_python38_log]
'use strict';

window.addEventListener('load', function () {
document.getElementById('sign-out').onclick = function () {
firebase.auth().signOut();
};

// FirebaseUI config.
var uiConfig = {
signInSuccessUrl: '/',
signInOptions: [
// Comment out any lines corresponding to providers you did not check in
// the Firebase console.
firebase.auth.GoogleAuthProvider.PROVIDER_ID,
firebase.auth.EmailAuthProvider.PROVIDER_ID,
//firebase.auth.FacebookAuthProvider.PROVIDER_ID,
//firebase.auth.TwitterAuthProvider.PROVIDER_ID,
//firebase.auth.GithubAuthProvider.PROVIDER_ID,
//firebase.auth.PhoneAuthProvider.PROVIDER_ID

],
// Terms of service url.
tosUrl: '<your-tos-url>'
};

firebase.auth().onAuthStateChanged(function (user) {
if (user) {
// User is signed in, so display the "sign out" button and login info.
document.getElementById('sign-out').hidden = false;
document.getElementById('login-info').hidden = false;
console.log(`Signed in as ${user.displayName} (${user.email})`);
user.getIdToken().then(function (token) {
// Add the token to the browser's cookies. The server will then be
// able to verify the token against the API.
// SECURITY NOTE: As cookies can easily be modified, only put the
// token (which is verified server-side) in a cookie; do not add other
// user information.
document.cookie = "token=" + token;
});
} else {
// User is signed out.
// Initialize the FirebaseUI Widget using Firebase.
var ui = new firebaseui.auth.AuthUI(firebase.auth());
// Show the Firebase login button.
ui.start('#firebaseui-auth-container', uiConfig);
// Update the login state indicators.
document.getElementById('sign-out').hidden = true;
document.getElementById('login-info').hidden = true;
// Clear the token cookie.
document.cookie = "token=";
}
}, function (error) {
console.log(error);
alert('Unable to log in: ' + error)
});
});
5 changes: 1 addition & 4 deletions templates/base.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,7 @@
<meta name="description" content="Advanced Development Coursework">
<meta name="author" content="Xin Lu">
<!--<link rel="icon" href="TODO">-->
<!-- Favicons (http://realfavicongenerator.net)
TODO -->
<!-- Open Graph
TODO -->
<!-- Favicons (http://realfavicongenerator.net) -->
<title>Advanced Development Coursework</title>
<!-- Bootstrap core CSS -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css">
Expand Down
89 changes: 89 additions & 0 deletions templates/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
<!doctype html>
<!--
Copyright 2021 Google LLC
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->

<html>
<head>
<title>Datastore and Firebase Auth Example</title>

<!-- See https://github.com/firebase/firebaseui-web. -->
<!-- [START gae_python38_auth_init_firebase] -->
<!-- [START gae_python3_auth_init_firebase] -->
<!-- *******************************************************************************************
* TODO(DEVELOPER): Paste the initialization snippet from:
* http://console.firebase.google.com > Overview > Add Firebase to your web app.
***************************************************************************************** -->
<!-- [END gae_python3_auth_init_firebase] -->
<!-- [END gae_python38_auth_init_firebase] -->

<!-- [START gae_python38_auth_include_firebaseui] -->
<!-- [START gae_python3_auth_include_firebaseui] -->
<script src="https://www.gstatic.com/firebasejs/ui/4.5.0/firebase-ui-auth.js"></script>
<link type="text/css" rel="stylesheet" href="https://www.gstatic.com/firebasejs/ui/4.5.0/firebase-ui-auth.css">
<!-- [END gae_python3_auth_include_firebaseui] -->
<!-- [END gae_python38_auth_include_firebaseui] -->
<script src="{{ url_for('static', filename='script.js') }}"></script>
<link type="text/css" rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">

</head>
<body>

<!-- [START gae_python38_auth_firebase_html] -->
<!-- [START gae_python3_auth_firebase_html] -->
<div id="firebaseui-auth-container"></div>

<button id="sign-out" hidden=true>Sign Out</button>

<div id="login-info" hidden=true>
<h2>Login info:</h2>
{% if user_data %}
<dl>
<dt>Name</dt><dd>{{ user_data['name'] }}</dd>
<dt>Email</dt><dd>{{ user_data['email'] }}</dd>
<dt>Last 10 visits</dt><dd>
{% for time in times %}
<p>{{ time['timestamp'] }}</p>
{% endfor %} </dd>
</dl>
{% elif error_message %}
<p>Error: {{ error_message }}</p>
{% endif %}
</div>
<!-- [END gae_python3_auth_firebase_html] -->
<!-- [END gae_python38_auth_firebase_html] -->
</body>
<!-- The core Firebase JS SDK is always required and must be listed first -->
<script src="https://www.gstatic.com/firebasejs/8.9.0/firebase-app.js"></script>

<script src="https://www.gstatic.com/firebasejs/8.9.0/firebase-auth.js"></script>

<!-- TODO: Add SDKs for Firebase products that you want to use
https://firebase.google.com/docs/web/setup#available-libraries -->

<script>
// Your web app's Firebase configuration
// For Firebase JS SDK v7.20.0 and later, measurementId is optional
var firebaseConfig = {
apiKey: "AIzaSyCJIw3wmaEl0dEb9wQsi8cGsQyEMJVPe9Q",
authDomain: "ad-364515.firebaseapp.com",
projectId: "ad-364515",
storageBucket: "ad-364515.appspot.com",
messagingSenderId: "998989263237",
appId: "1:998989263237:web:46ccb72401634a2e4b14ec"
};
// Initialize Firebase
firebase.initializeApp(firebaseConfig);
</script>


</html>