Skip to content

Commit 7dd92eb

Browse files
Refactor code and add new features
1 parent dc9dd41 commit 7dd92eb

File tree

315 files changed

+2322
-868
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

315 files changed

+2322
-868
lines changed

.env

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,25 @@
1+
# True for development, False for production
12
DEBUG=True
2-
SECRET_KEY=sdgfsjdgfjk
33

4+
# Flask ENV
45
FLASK_APP=run.py
5-
FLASK_ENV=development
6+
FLASK_DEBUG=1
7+
8+
# If not provided, a random one is generated
9+
# SECRET_KEY=<YOUR_SUPER_KEY_HERE>
10+
11+
# If DEBUG=False (production mode)
12+
# DB_ENGINE=mysql
13+
# DB_NAME=appseed_db
14+
# DB_HOST=localhost
15+
# DB_PORT=3306
16+
# DB_USERNAME=appseed_db_usr
17+
# DB_PASS=<STRONG_PASS>
18+
19+
# SOCIAL AUTH Github
20+
# GITHUB_ID=YOUR_GITHUB_ID
21+
# GITHUB_SECRET=YOUR_GITHUB_SECRET
22+
23+
# SOCIAL AUTH Google
24+
# GOOGLE_ID=YOUR_GOOGLE_ID
25+
# GOOGLE_SECRET=YOUR_GOOGLE_SECRET

apps/__init__.py

Lines changed: 12 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,10 @@
44
"""
55

66
import os
7-
8-
from flask import Flask, send_from_directory
7+
from flask import Flask
98
from flask_login import LoginManager
109
from flask_sqlalchemy import SQLAlchemy
1110
from importlib import import_module
12-
from celery import Celery, Task
13-
from apps.config import Config
1411

1512
db = SQLAlchemy()
1613
login_manager = LoginManager()
@@ -20,73 +17,29 @@ def register_extensions(app):
2017
login_manager.init_app(app)
2118

2219
def register_blueprints(app):
23-
for module_name in ('authentication', 'home', ):
20+
for module_name in ('authentication', 'home', 'dyn_dt', 'charts', ):
2421
module = import_module('apps.{}.routes'.format(module_name))
2522
app.register_blueprint(module.blueprint)
2623

27-
def configure_database(app):
28-
29-
@app.before_first_request
30-
def initialize_database():
31-
try:
32-
db.create_all()
33-
except Exception as e:
34-
35-
print('> Error: DBMS Exception: ' + str(e) )
36-
37-
# fallback to SQLite
38-
basedir = os.path.abspath(os.path.dirname(__file__))
39-
app.config['SQLALCHEMY_DATABASE_URI'] = SQLALCHEMY_DATABASE_URI = 'sqlite:///' + os.path.join(basedir, 'db.sqlite3')
40-
41-
print('> Fallback to SQLite ')
42-
db.create_all()
43-
44-
@app.teardown_request
45-
def shutdown_session(exception=None):
46-
db.session.remove()
47-
48-
49-
def celery_init_app(app: Flask) -> Celery:
50-
class FlaskTask(Task):
51-
def __call__(self, *args: object, **kwargs: object) -> object:
52-
with app.app_context():
53-
return self.run(*args, **kwargs)
54-
55-
celery_app = Celery(app.name, task_cls=FlaskTask)
56-
celery_app.config_from_object(app.config["CELERY"])
57-
celery_app.set_default()
58-
app.extensions["celery"] = celery_app
59-
return celery_app
60-
24+
from apps.authentication.oauth import github_blueprint, google_blueprint
6125

6226
def create_app(config):
6327

64-
# Read debug flag
65-
DEBUG = (os.getenv('DEBUG', 'False') == 'True')
66-
6728
# Contextual
68-
static_prefix = '/static' if DEBUG else '/'
29+
static_prefix = '/static'
30+
templates_dir = os.path.dirname(config.BASE_DIR)
6931

70-
app = Flask(__name__,static_url_path=static_prefix)
32+
TEMPLATES_FOLDER = os.path.join(templates_dir,'templates')
33+
STATIC_FOLDER = os.path.join(templates_dir,'static')
7134

72-
@app.route('/media/<path:filename>')
73-
def media_files(filename):
74-
return send_from_directory(Config.MEDIA_FOLDER, filename)
35+
print(' > TEMPLATES_FOLDER: ' + TEMPLATES_FOLDER)
36+
print(' > STATIC_FOLDER: ' + STATIC_FOLDER)
7537

76-
app.config.from_mapping(
77-
CELERY=dict(
78-
broker_url="redis://localhost",
79-
result_backend="redis://localhost",
80-
task_ignore_result=True,
81-
),
82-
)
83-
# app.config.from_prefixed_env()
84-
celery_init_app(app)
38+
app = Flask(__name__, static_url_path=static_prefix, template_folder=TEMPLATES_FOLDER, static_folder=STATIC_FOLDER)
8539

8640
app.config.from_object(config)
8741
register_extensions(app)
8842
register_blueprints(app)
89-
configure_database(app)
90-
91-
43+
app.register_blueprint(github_blueprint, url_prefix="/login")
44+
app.register_blueprint(google_blueprint, url_prefix="/login")
9245
return app

apps/authentication/models.py

Lines changed: 51 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,28 @@
33
Copyright (c) 2019 - present AppSeed.us
44
"""
55

6-
import enum
76
from flask_login import UserMixin
7+
8+
from sqlalchemy.exc import SQLAlchemyError, IntegrityError
9+
from flask_dance.consumer.storage.sqla import OAuthConsumerMixin
10+
811
from apps import db, login_manager
912
from apps.authentication.util import hash_pass
10-
from sqlalchemy.orm import relationship
11-
from sqlalchemy import event
1213

1314
class Users(db.Model, UserMixin):
1415

15-
__tablename__ = 'Users'
16+
__tablename__ = 'users'
17+
18+
id = db.Column(db.Integer, primary_key=True)
19+
username = db.Column(db.String(64), unique=True)
20+
email = db.Column(db.String(64), unique=True)
21+
password = db.Column(db.LargeBinary)
22+
bio = db.Column(db.Text(), nullable=True)
1623

17-
id = db.Column(db.Integer, primary_key=True)
18-
username = db.Column(db.String(64), unique=True)
19-
email = db.Column(db.String(64), unique=True)
20-
password = db.Column(db.LargeBinary)
24+
oauth_github = db.Column(db.String(100), nullable=True)
25+
oauth_google = db.Column(db.String(100), nullable=True)
26+
27+
readonly_fields = ["id", "username", "email", "oauth_github", "oauth_google"]
2128

2229
def __init__(self, **kwargs):
2330
for property, value in kwargs.items():
@@ -36,52 +43,50 @@ def __init__(self, **kwargs):
3643
def __repr__(self):
3744
return str(self.username)
3845

46+
@classmethod
47+
def find_by_email(cls, email: str) -> "Users":
48+
return cls.query.filter_by(email=email).first()
49+
50+
@classmethod
51+
def find_by_username(cls, username: str) -> "Users":
52+
return cls.query.filter_by(username=username).first()
53+
54+
@classmethod
55+
def find_by_id(cls, _id: int) -> "Users":
56+
return cls.query.filter_by(id=_id).first()
57+
58+
def save(self) -> None:
59+
try:
60+
db.session.add(self)
61+
db.session.commit()
62+
63+
except SQLAlchemyError as e:
64+
db.session.rollback()
65+
db.session.close()
66+
error = str(e.__dict__['orig'])
67+
raise IntegrityError(error, 422)
68+
69+
def delete_from_db(self) -> None:
70+
try:
71+
db.session.delete(self)
72+
db.session.commit()
73+
except SQLAlchemyError as e:
74+
db.session.rollback()
75+
db.session.close()
76+
error = str(e.__dict__['orig'])
77+
raise IntegrityError(error, 422)
78+
return
3979

4080
@login_manager.user_loader
4181
def user_loader(id):
4282
return Users.query.filter_by(id=id).first()
4383

44-
4584
@login_manager.request_loader
4685
def request_loader(request):
4786
username = request.form.get('username')
4887
user = Users.query.filter_by(username=username).first()
4988
return user if user else None
5089

51-
52-
class Role(enum.Enum):
53-
ADMIN = 1
54-
USER = 2
55-
56-
def __str__(self):
57-
return str(self.value)
58-
59-
class Profile(db.Model):
60-
id = db.Column(db.Integer, primary_key=True)
61-
role = db.Column(db.Enum(Role), default=Role.USER)
62-
full_name = db.Column(db.String(255), name="full_name", nullable=True)
63-
address = db.Column(db.String(255), name="address", nullable=True)
64-
city = db.Column(db.String(255), name="city", nullable=True)
65-
country = db.Column(db.String(255), name="country", nullable=True)
66-
zip = db.Column(db.String(255), name="zip", nullable=True)
67-
phone = db.Column(db.String(255), name="phone", nullable=True)
68-
avatar = db.Column(db.String(1000), nullable=True)
69-
user_id = db.Column(db.Integer, db.ForeignKey('Users.id', ondelete='cascade'), nullable=False)
70-
user = relationship("Users", backref="profile")
71-
72-
@classmethod
73-
def find_by_id(cls, _id: int) -> "Profile":
74-
return cls.query.filter_by(id=_id).first()
75-
76-
@classmethod
77-
def find_by_user_id(cls, _id: int):
78-
return cls.query.filter_by(user_id=_id).first()
79-
80-
81-
# create profile
82-
def create_profile_for_user(mapper, connection, user):
83-
connection.execute(Profile.__table__.insert().values(
84-
user_id=user.id,
85-
))
86-
87-
event.listen(Users, 'after_insert', create_profile_for_user)
90+
class OAuth(OAuthConsumerMixin, db.Model):
91+
user_id = db.Column(db.Integer, db.ForeignKey("users.id", ondelete="cascade"), nullable=False)
92+
user = db.relationship(Users)

apps/authentication/oauth.py

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
# -*- encoding: utf-8 -*-
2+
"""
3+
Copyright (c) 2019 - present AppSeed.us
4+
"""
5+
6+
import os
7+
from flask import current_app as app
8+
from flask_login import current_user, login_user
9+
from flask_dance.consumer import oauth_authorized
10+
from flask_dance.contrib.github import github, make_github_blueprint
11+
from flask_dance.contrib.google import google, make_google_blueprint
12+
from flask_dance.consumer.storage.sqla import SQLAlchemyStorage
13+
from sqlalchemy.orm.exc import NoResultFound
14+
from apps.config import Config
15+
from .models import Users, db, OAuth
16+
from flask import redirect, url_for
17+
from flask import flash
18+
19+
github_blueprint = make_github_blueprint(
20+
client_id=Config.GITHUB_ID,
21+
client_secret=Config.GITHUB_SECRET,
22+
scope = 'user',
23+
storage=SQLAlchemyStorage(
24+
OAuth,
25+
db.session,
26+
user=current_user,
27+
user_required=False,
28+
),
29+
)
30+
31+
@oauth_authorized.connect_via(github_blueprint)
32+
def github_logged_in(blueprint, token):
33+
info = github.get("/user")
34+
35+
if info.ok:
36+
37+
account_info = info.json()
38+
username = account_info["login"]
39+
40+
query = Users.query.filter_by(oauth_github=username)
41+
try:
42+
43+
user = query.one()
44+
login_user(user)
45+
46+
except NoResultFound:
47+
48+
# Save to db
49+
user = Users()
50+
user.username = '(gh)' + username
51+
user.oauth_github = username
52+
53+
# Save current user
54+
db.session.add(user)
55+
db.session.commit()
56+
57+
login_user(user)
58+
59+
# Google
60+
61+
google_blueprint = make_google_blueprint(
62+
client_id=Config.GOOGLE_ID,
63+
client_secret=Config.GOOGLE_SECRET,
64+
scope=[
65+
"openid",
66+
"https://www.googleapis.com/auth/userinfo.email",
67+
"https://www.googleapis.com/auth/userinfo.profile",
68+
],
69+
storage=SQLAlchemyStorage(
70+
OAuth,
71+
db.session,
72+
user=current_user,
73+
user_required=False,
74+
),
75+
)
76+
77+
@oauth_authorized.connect_via(google_blueprint)
78+
def google_logged_in(blueprint, token):
79+
info = google.get("/oauth2/v1/userinfo")
80+
81+
if info.ok:
82+
account_info = info.json()
83+
username = account_info["given_name"]
84+
email = account_info["email"]
85+
86+
query = Users.query.filter_by(oauth_google=username)
87+
try:
88+
89+
user = query.one()
90+
login_user(user)
91+
92+
except NoResultFound:
93+
# Save to db
94+
user = Users()
95+
user.username = '(google)' + username
96+
user.oauth_google = username
97+
user.email = email
98+
99+
# Save current user
100+
db.session.add(user)
101+
db.session.commit()
102+
103+
login_user(user)

0 commit comments

Comments
 (0)