├── .dockerignore ├── .env ├── .gitignore ├── CHANGELOG.md ├── Dockerfile ├── LICENSE.md ├── README.md ├── apps ├── __init__.py ├── authentication │ ├── __init__.py │ ├── forms.py │ ├── models.py │ ├── oauth.py │ ├── routes.py │ └── util.py ├── charts │ ├── __init__.py │ └── routes.py ├── config.py ├── dyn_dt │ ├── __init__.py │ ├── routes.py │ └── utils.py ├── exceptions │ └── exception.py ├── helpers.py ├── home │ ├── __init__.py │ └── routes.py ├── messages.py ├── models.py └── tasks.py ├── build.sh ├── docker-compose.yml ├── env.sample ├── gulpfile.js ├── gunicorn-cfg.py ├── media ├── flask-black-dashboard-card-low.jpg ├── flask-black-dashboard-card-low.png ├── flask-black-dashboard-card.jpg ├── flask-black-dashboard-card.png ├── flask-black-dashboard-content-image-low.png ├── flask-black-dashboard-content-image.png ├── flask-black-dashboard-screen-1-low.png ├── flask-black-dashboard-screen-1.png ├── flask-black-dashboard-screen-2-low.png ├── flask-black-dashboard-screen-2.png ├── flask-black-dashboard-screen-3-low.png ├── flask-black-dashboard-screen-3.png ├── flask-black-dashboard-screen-login-low.png ├── flask-black-dashboard-screen-login.png ├── flask-black-dashboard-screen-low.png ├── flask-black-dashboard-screen-register-low.png ├── flask-black-dashboard-screen-register.png └── flask-black-dashboard-screen.png ├── nginx └── appseed-app.conf ├── package.json ├── postcss.config.js ├── render.yaml ├── requirements.txt ├── run.py ├── static └── assets │ ├── css │ ├── black-dashboard.css │ ├── black-dashboard.css.map │ ├── black-dashboard.min.css │ ├── nucleo-icons.css │ └── theme-switcher.css │ ├── demo │ ├── demo.css │ └── demo.js │ ├── fonts │ ├── nucleo.eot │ ├── nucleo.ttf │ ├── nucleo.woff │ └── nucleo.woff2 │ ├── img │ ├── 12345.png │ ├── anime3.png │ ├── anime6.png │ ├── apple-icon.png │ ├── bg5.jpg │ ├── csv.png │ ├── default-avatar.png │ ├── emilyz.jpg │ ├── export.png │ ├── favicon.png │ ├── header.jpg │ ├── img_3115.jpg │ ├── james.jpg │ ├── mike.jpg │ └── now-logo.png │ ├── js │ ├── black-dashboard.js │ ├── black-dashboard.js.map │ ├── black-dashboard.min.js │ ├── core │ │ ├── bootstrap.min.js │ │ ├── jquery.min.js │ │ └── popper.min.js │ ├── plugins │ │ ├── bootstrap-notify.js │ │ ├── chartjs.min.js │ │ └── perfect-scrollbar.jquery.min.js │ └── themeSettings.js │ └── scss │ ├── black-dashboard.scss │ └── black-dashboard │ ├── _buttons.scss │ ├── _typography.scss │ ├── bootstrap │ ├── _alert.scss │ ├── _badge.scss │ ├── _breadcrumb.scss │ ├── _button-group.scss │ ├── _buttons.scss │ ├── _card.scss │ ├── _carousel.scss │ ├── _close.scss │ ├── _code.scss │ ├── _custom-forms.scss │ ├── _dropdown.scss │ ├── _forms.scss │ ├── _functions.scss │ ├── _grid.scss │ ├── _images.scss │ ├── _input-group.scss │ ├── _jumbotron.scss │ ├── _list-group.scss │ ├── _media.scss │ ├── _mixins.scss │ ├── _modal.scss │ ├── _nav.scss │ ├── _navbar.scss │ ├── _pagination.scss │ ├── _popover.scss │ ├── _print.scss │ ├── _progress.scss │ ├── _reboot.scss │ ├── _root.scss │ ├── _spinners.scss │ ├── _tables.scss │ ├── _toasts.scss │ ├── _tooltip.scss │ ├── _transitions.scss │ ├── _type.scss │ ├── _utilities.scss │ ├── _variables.scss │ ├── bootstrap-grid.scss │ ├── bootstrap-reboot.scss │ ├── bootstrap.scss │ ├── mixins │ │ ├── _alert.scss │ │ ├── _background-variant.scss │ │ ├── _badge.scss │ │ ├── _border-radius.scss │ │ ├── _box-shadow.scss │ │ ├── _breakpoints.scss │ │ ├── _buttons.scss │ │ ├── _caret.scss │ │ ├── _clearfix.scss │ │ ├── _deprecate.scss │ │ ├── _float.scss │ │ ├── _forms.scss │ │ ├── _gradients.scss │ │ ├── _grid-framework.scss │ │ ├── _grid.scss │ │ ├── _hover.scss │ │ ├── _image.scss │ │ ├── _list-group.scss │ │ ├── _lists.scss │ │ ├── _nav-divider.scss │ │ ├── _pagination.scss │ │ ├── _reset-text.scss │ │ ├── _resize.scss │ │ ├── _screen-reader.scss │ │ ├── _size.scss │ │ ├── _table-row.scss │ │ ├── _text-emphasis.scss │ │ ├── _text-hide.scss │ │ ├── _text-truncate.scss │ │ ├── _transition.scss │ │ └── _visibility.scss │ ├── utilities │ │ ├── _align.scss │ │ ├── _background.scss │ │ ├── _borders.scss │ │ ├── _clearfix.scss │ │ ├── _display.scss │ │ ├── _embed.scss │ │ ├── _flex.scss │ │ ├── _float.scss │ │ ├── _overflow.scss │ │ ├── _position.scss │ │ ├── _screenreaders.scss │ │ ├── _shadows.scss │ │ ├── _sizing.scss │ │ ├── _spacing.scss │ │ ├── _stretched-link.scss │ │ ├── _text.scss │ │ └── _visibility.scss │ └── vendor │ │ └── _rfs.scss │ ├── custom │ ├── _alerts.scss │ ├── _buttons.scss │ ├── _card.scss │ ├── _checkboxes-radio.scss │ ├── _dropdown.scss │ ├── _example-pages.scss │ ├── _fixed-plugin.scss │ ├── _footer.scss │ ├── _forms.scss │ ├── _functions.scss │ ├── _images.scss │ ├── _input-group.scss │ ├── _misc.scss │ ├── _mixins.scss │ ├── _modal.scss │ ├── _navbar.scss │ ├── _rtl.scss │ ├── _sidebar-and-main-panel.scss │ ├── _tables.scss │ ├── _type.scss │ ├── _utilities.scss │ ├── _variables.scss │ ├── _white-content.scss │ ├── cards │ │ ├── _card-chart.scss │ │ ├── _card-map.scss │ │ ├── _card-plain.scss │ │ ├── _card-task.scss │ │ └── _card-user.scss │ ├── mixins │ │ ├── _alert.scss │ │ ├── _background-variant.scss │ │ ├── _badges.scss │ │ ├── _buttons.scss │ │ ├── _dropdown.scss │ │ ├── _forms.scss │ │ ├── _icon.scss │ │ ├── _inputs.scss │ │ ├── _modals.scss │ │ ├── _page-header.scss │ │ ├── _popovers.scss │ │ ├── _vendor-prefixes.scss │ │ ├── _wizard.scss │ │ └── opacity.scss │ ├── utilities │ │ ├── _backgrounds.scss │ │ ├── _floating.scss │ │ ├── _helper.scss │ │ ├── _position.scss │ │ ├── _shadows.scss │ │ ├── _sizing.scss │ │ ├── _spacing.scss │ │ ├── _text.scss │ │ └── _transform.scss │ └── vendor │ │ ├── _plugin-animate-bootstrap-notify.scss │ │ └── _plugin-perfect-scrollbar.scss │ └── plugins │ └── _plugin-perfect-scrollbar.scss ├── templates ├── .gitkeep ├── accounts │ ├── login.html │ └── register.html ├── charts │ └── index.html ├── dyn_dt │ ├── index.html │ └── model.html ├── home │ ├── icons.html │ ├── index.html │ ├── login.html │ ├── map.html │ ├── notifications.html │ ├── page-403.html │ ├── page-404.html │ ├── page-500.html │ ├── register.html │ ├── rtl.html │ ├── tables.html │ ├── typography.html │ └── user.html ├── includes │ ├── fixed-plugin.html │ ├── footer-fullscreen.html │ ├── footer.html │ ├── items-table.html │ ├── navigation-fullscreen.html │ ├── navigation-rtl.html │ ├── navigation.html │ ├── scripts.html │ ├── sidebar-rtl.html │ └── sidebar.html └── layouts │ ├── base-fullscreen.html │ ├── base-rtl.html │ └── base.html └── vite.config.js /.dockerignore: -------------------------------------------------------------------------------- 1 | .git 2 | __pycache__ 3 | *.pyc 4 | *.pyo 5 | *.pyd -------------------------------------------------------------------------------- /.env: -------------------------------------------------------------------------------- 1 | # True for development, False for production 2 | DEBUG=True 3 | 4 | # Flask ENV 5 | FLASK_APP=run.py 6 | FLASK_DEBUG=1 7 | 8 | # If not provided, a random one is generated 9 | # SECRET_KEY= 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= 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 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | 5 | # tests and coverage 6 | *.pytest_cache 7 | .coverage 8 | 9 | # database & logs 10 | *.db 11 | *.sqlite3 12 | *.log 13 | 14 | # venv 15 | env 16 | venv 17 | 18 | # other 19 | .DS_Store 20 | 21 | # sphinx docs 22 | _build 23 | _static 24 | _templates 25 | 26 | # javascript 27 | package-lock.json 28 | .vscode/symbols.json 29 | 30 | apps/static/assets/node_modules 31 | apps/static/assets/yarn.lock 32 | apps/static/assets/.temp 33 | 34 | migrations 35 | node_modules 36 | yarn.lock 37 | 38 | README_bk.md 39 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.10 2 | 3 | # set environment variables 4 | ENV PYTHONDONTWRITEBYTECODE 1 5 | ENV PYTHONUNBUFFERED 1 6 | ENV FLASK_APP run.py 7 | ENV DEBUG True 8 | 9 | COPY requirements.txt . 10 | 11 | # install python dependencies 12 | RUN pip install --upgrade pip 13 | RUN pip install --no-cache-dir -r requirements.txt 14 | 15 | COPY env.sample .env 16 | 17 | COPY . . 18 | 19 | RUN flask db init 20 | RUN flask db migrate 21 | RUN flask db upgrade 22 | 23 | # gunicorn 24 | CMD ["gunicorn", "--config", "gunicorn-cfg.py", "run:app"] 25 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # MIT License 2 | 3 | Copyright (c) 2019 - present [AppSeed](http://appseed.us/) 4 | 5 |
6 | 7 | ## Licensing Information 8 | 9 |
10 | 11 | | Item | - | 12 | | ---------------------------------- | --- | 13 | | License Type | MIT | 14 | | Use for print | **YES** | 15 | | Create single personal website/app | **YES** | 16 | | Create single website/app for client | **YES** | 17 | | Create multiple website/apps for clients | **YES** | 18 | | Create multiple SaaS applications | **YES** | 19 | | End-product paying users | **YES** | 20 | | Product sale | **YES** | 21 | | Remove footer credits | **YES** | 22 | | --- | --- | 23 | | Remove copyright mentions from source code | NO | 24 | | Production deployment assistance | NO | 25 | | Create HTML/CSS template for sale | NO | 26 | | Create Theme/Template for CMS for sale | NO | 27 | | Separate sale of our UI Elements | NO | 28 | 29 |
30 | 31 | --- 32 | For more information regarding licensing, please contact the AppSeed Service < *support@appseed.us* > 33 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [Flask Black Dashboard](https://app-generator.dev/product/black-dashboard/flask/) 2 | 3 | Open-source **Flask Starter with Black Dashboard Design**, an open-source iconic `Bootstrap` design. 4 | The product is designed to deliver the best possible user experience with highly customizable feature-rich pages. 5 | 6 | - 👉 [Flask Black Dashboard](https://app-generator.dev/product/black-dashboard/flask/) - `Product Page` 7 | - 👉 [Flask Black Dashboard](https://flask-black.onrender.com/) - `LIVE Demo` 8 | - 👉 [Flask Black Dashboard](https://app-generator.dev/docs/products/flask/black-dashboard/index.html) - `Complete Information` and Support Links 9 | - [Getting Started with Flask](https://app-generator.dev/docs/technologies/flask/index.html) - a `comprehensive tutorial` 10 | - `Configuration`: Install Tailwind/Flowbite, Prepare Environment, Setting up the Database 11 | - `Start with Docker` 12 | - `Manual Build` 13 | - `Start the project` 14 | - `Deploy on Render` 15 | 16 |
17 | 18 | ## Features 19 | 20 | - Simple, Easy-to-Extend codebase, [Blueprint Pattern](https://app-generator.dev/blog/flask-blueprints-a-developers-guide/) 21 | - [Black Dashboard](https://app-generator.dev/docs/templates/bootstrap/black-dashboard.html) Full Integration 22 | - [Bootstrap](https://app-generator.dev/docs/templates/bootstrap/index.html) 5 Styling 23 | - Auth: Session Based 24 | - CI/CD integration for [Render](https://app-generator.dev/docs/deployment/render/index.html) 25 | - Deployment: Docker, Flask-Minify 26 | 27 | ![Flask Black Dashboard - Open-Source Django Starter ](https://user-images.githubusercontent.com/51070104/196730732-dda1794b-93ce-48cb-bc5c-182411495512.png) 28 | 29 |
30 | 31 | ## Deploy on `Render` 32 | 33 | [![Deploy to Render](https://render.com/images/deploy-to-render-button.svg)](https://render.com/deploy) 34 | 35 |
36 | 37 | ## [Black Dashboard PRO Version](https://app-generator.dev/product/black-dashboard-pro/flask/) 38 | 39 | > The premium version provides more features, priority on support, and is more often updated - [Live Demo](https://flask-black-dashboard-enh.appseed-srv1.com/). 40 | 41 | - **Simple, Easy-to-Extend** Codebase 42 | - **Black Dashboard** Design - PRO Version 43 | - Bootstrap 4 CSS 44 | - **Deployment-Ready** for Render 45 | 46 | ![Flask Black PRO - Premium Flask starter built with Black Dashboard design from Creative-Tim](https://github.com/user-attachments/assets/63c1ea5b-6c8b-4e67-8e07-156c3e06895f) 47 | 48 |
49 | 50 | ### Customize with [Flask App Generator](https://app-generator.dev/tools/flask-generator/) 51 | 52 | - Access the [App Generator](https://app-generator.dev/tools/flask-generator/) page 53 | - Select the preferred design 54 | - (Optional) Design Database: edit models and fields 55 | - (Optional) Edit the fields for the extended user model 56 | - (Optional) Enable OAuth for GitHub 57 | - (Optional) Add Celery (async tasks) 58 | - (Optional) Enable Dynamic Tables Module 59 | - Docker Scripts 60 | - Render CI/Cd Scripts 61 | 62 | **The generated Flask project is available as a ZIP Archive and also uploaded to GitHub.** 63 | 64 |
65 | 66 | --- 67 | [Flask Black Dashboard](https://app-generator.dev/product/black-dashboard/flask/) - Open-Source **Flask** Starter provided by [App Generator](https://app-generator.dev) 68 | -------------------------------------------------------------------------------- /apps/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | """ 3 | Copyright (c) 2019 - present AppSeed.us 4 | """ 5 | 6 | import os 7 | from flask import Flask 8 | from flask_login import LoginManager 9 | from flask_sqlalchemy import SQLAlchemy 10 | from importlib import import_module 11 | 12 | db = SQLAlchemy() 13 | login_manager = LoginManager() 14 | 15 | def register_extensions(app): 16 | db.init_app(app) 17 | login_manager.init_app(app) 18 | 19 | def register_blueprints(app): 20 | for module_name in ('authentication', 'home', 'dyn_dt', 'charts', ): 21 | module = import_module('apps.{}.routes'.format(module_name)) 22 | app.register_blueprint(module.blueprint) 23 | 24 | from apps.authentication.oauth import github_blueprint, google_blueprint 25 | 26 | def create_app(config): 27 | 28 | # Contextual 29 | static_prefix = '/static' 30 | templates_dir = os.path.dirname(config.BASE_DIR) 31 | 32 | TEMPLATES_FOLDER = os.path.join(templates_dir,'templates') 33 | STATIC_FOLDER = os.path.join(templates_dir,'static') 34 | 35 | print(' > TEMPLATES_FOLDER: ' + TEMPLATES_FOLDER) 36 | print(' > STATIC_FOLDER: ' + STATIC_FOLDER) 37 | 38 | app = Flask(__name__, static_url_path=static_prefix, template_folder=TEMPLATES_FOLDER, static_folder=STATIC_FOLDER) 39 | 40 | app.config.from_object(config) 41 | register_extensions(app) 42 | register_blueprints(app) 43 | app.register_blueprint(github_blueprint, url_prefix="/login") 44 | app.register_blueprint(google_blueprint, url_prefix="/login") 45 | return app 46 | -------------------------------------------------------------------------------- /apps/authentication/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | """ 3 | Copyright (c) 2019 - present AppSeed.us 4 | """ 5 | 6 | from flask import Blueprint 7 | 8 | blueprint = Blueprint( 9 | 'authentication_blueprint', 10 | __name__, 11 | url_prefix='' 12 | ) 13 | -------------------------------------------------------------------------------- /apps/authentication/forms.py: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | """ 3 | Copyright (c) 2019 - present AppSeed.us 4 | """ 5 | 6 | from flask_wtf import FlaskForm 7 | from wtforms import StringField, PasswordField 8 | from wtforms.validators import Email, DataRequired 9 | 10 | # login and registration 11 | 12 | 13 | class LoginForm(FlaskForm): 14 | username = StringField('Username', 15 | id='username_login', 16 | validators=[DataRequired()]) 17 | password = PasswordField('Password', 18 | id='pwd_login', 19 | validators=[DataRequired()]) 20 | 21 | 22 | class CreateAccountForm(FlaskForm): 23 | username = StringField('Username', 24 | id='username_create', 25 | validators=[DataRequired()]) 26 | email = StringField('Email', 27 | id='email_create', 28 | validators=[DataRequired(), Email()]) 29 | password = PasswordField('Password', 30 | id='pwd_create', 31 | validators=[DataRequired()]) 32 | -------------------------------------------------------------------------------- /apps/authentication/models.py: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | """ 3 | Copyright (c) 2019 - present AppSeed.us 4 | """ 5 | 6 | from flask_login import UserMixin 7 | 8 | from sqlalchemy.exc import SQLAlchemyError, IntegrityError 9 | from flask_dance.consumer.storage.sqla import OAuthConsumerMixin 10 | 11 | from apps import db, login_manager 12 | from apps.authentication.util import hash_pass 13 | 14 | class Users(db.Model, UserMixin): 15 | 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) 23 | 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"] 28 | 29 | def __init__(self, **kwargs): 30 | for property, value in kwargs.items(): 31 | # depending on whether value is an iterable or not, we must 32 | # unpack it's value (when **kwargs is request.form, some values 33 | # will be a 1-element list) 34 | if hasattr(value, '__iter__') and not isinstance(value, str): 35 | # the ,= unpack of a singleton fails PEP8 (travis flake8 test) 36 | value = value[0] 37 | 38 | if property == 'password': 39 | value = hash_pass(value) # we need bytes here (not plain str) 40 | 41 | setattr(self, property, value) 42 | 43 | def __repr__(self): 44 | return str(self.username) 45 | 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 79 | 80 | @login_manager.user_loader 81 | def user_loader(id): 82 | return Users.query.filter_by(id=id).first() 83 | 84 | @login_manager.request_loader 85 | def request_loader(request): 86 | username = request.form.get('username') 87 | user = Users.query.filter_by(username=username).first() 88 | return user if user else None 89 | 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) 93 | -------------------------------------------------------------------------------- /apps/authentication/oauth.py: -------------------------------------------------------------------------------- 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) 104 | -------------------------------------------------------------------------------- /apps/authentication/util.py: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | """ 3 | Copyright (c) 2019 - present AppSeed.us 4 | """ 5 | 6 | import os 7 | import hashlib 8 | import binascii 9 | 10 | # Inspiration -> https://www.vitoshacademy.com/hashing-passwords-in-python/ 11 | 12 | 13 | def hash_pass(password): 14 | """Hash a password for storing.""" 15 | 16 | salt = hashlib.sha256(os.urandom(60)).hexdigest().encode('ascii') 17 | pwdhash = hashlib.pbkdf2_hmac('sha512', password.encode('utf-8'), 18 | salt, 100000) 19 | pwdhash = binascii.hexlify(pwdhash) 20 | return (salt + pwdhash) # return bytes 21 | 22 | 23 | def verify_pass(provided_password, stored_password): 24 | """Verify a stored password against one provided by user""" 25 | 26 | stored_password = stored_password.decode('ascii') 27 | salt = stored_password[:64] 28 | stored_password = stored_password[64:] 29 | pwdhash = hashlib.pbkdf2_hmac('sha512', 30 | provided_password.encode('utf-8'), 31 | salt.encode('ascii'), 32 | 100000) 33 | pwdhash = binascii.hexlify(pwdhash).decode('ascii') 34 | return pwdhash == stored_password 35 | -------------------------------------------------------------------------------- /apps/charts/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | """ 3 | Copyright (c) 2019 - present AppSeed.us 4 | """ 5 | 6 | from flask import Blueprint 7 | 8 | blueprint = Blueprint( 9 | 'charts_blueprint', 10 | __name__, 11 | url_prefix='' 12 | ) 13 | -------------------------------------------------------------------------------- /apps/charts/routes.py: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | """ 3 | Copyright (c) 2019 - present AppSeed.us 4 | """ 5 | 6 | from apps.charts import blueprint 7 | from flask import render_template 8 | from apps.models import Product 9 | 10 | @blueprint.route('/charts') 11 | def charts(): 12 | products = [{'name': product.name, 'price': product.price} for product in Product.get_list()] 13 | return render_template('charts/index.html', segment='charts', products=products) -------------------------------------------------------------------------------- /apps/config.py: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | """ 3 | Copyright (c) 2019 - present AppSeed.us 4 | """ 5 | 6 | import os 7 | from pathlib import Path 8 | 9 | class Config(object): 10 | 11 | BASE_DIR = Path(__file__).resolve().parent 12 | 13 | USERS_ROLES = { 'ADMIN' :1 , 'USER' : 2 } 14 | USERS_STATUS = { 'ACTIVE' :1 , 'SUSPENDED' : 2 } 15 | 16 | # celery 17 | CELERY_BROKER_URL = "redis://localhost:6379" 18 | CELERY_RESULT_BACKEND = "redis://localhost:6379" 19 | CELERY_HOSTMACHINE = "celery@app-generator" 20 | 21 | # Set up the App SECRET_KEY 22 | SECRET_KEY = os.getenv('SECRET_KEY', 'S3cret_999') 23 | 24 | # Social AUTH context 25 | SOCIAL_AUTH_GITHUB = False 26 | 27 | GITHUB_ID = os.getenv('GITHUB_ID' , None) 28 | GITHUB_SECRET = os.getenv('GITHUB_SECRET', None) 29 | 30 | # Enable/Disable Github Social Login 31 | if GITHUB_ID and GITHUB_SECRET: 32 | SOCIAL_AUTH_GITHUB = True 33 | 34 | GOOGLE_ID = os.getenv('GOOGLE_ID' , None) 35 | GOOGLE_SECRET = os.getenv('GOOGLE_SECRET', None) 36 | 37 | # Enable/Disable Google Social Login 38 | if GOOGLE_ID and GOOGLE_SECRET: 39 | SOCIAL_AUTH_GOOGLE = True 40 | 41 | SQLALCHEMY_TRACK_MODIFICATIONS = False 42 | 43 | DB_ENGINE = os.getenv('DB_ENGINE' , None) 44 | DB_USERNAME = os.getenv('DB_USERNAME' , None) 45 | DB_PASS = os.getenv('DB_PASS' , None) 46 | DB_HOST = os.getenv('DB_HOST' , None) 47 | DB_PORT = os.getenv('DB_PORT' , None) 48 | DB_NAME = os.getenv('DB_NAME' , None) 49 | 50 | USE_SQLITE = True 51 | 52 | # try to set up a Relational DBMS 53 | if DB_ENGINE and DB_NAME and DB_USERNAME: 54 | 55 | try: 56 | 57 | # Relational DBMS: PSQL, MySql 58 | SQLALCHEMY_DATABASE_URI = '{}://{}:{}@{}:{}/{}'.format( 59 | DB_ENGINE, 60 | DB_USERNAME, 61 | DB_PASS, 62 | DB_HOST, 63 | DB_PORT, 64 | DB_NAME 65 | ) 66 | 67 | USE_SQLITE = False 68 | 69 | except Exception as e: 70 | 71 | print('> Error: DBMS Exception: ' + str(e) ) 72 | print('> Fallback to SQLite ') 73 | 74 | if USE_SQLITE: 75 | 76 | # This will create a file in FOLDER 77 | SQLALCHEMY_DATABASE_URI = 'sqlite:///' + os.path.join(BASE_DIR, 'db.sqlite3') 78 | 79 | DYNAMIC_DATATB = { 80 | "products": "apps.models.Product" 81 | } 82 | 83 | CDN_DOMAIN = os.getenv('CDN_DOMAIN') 84 | CDN_HTTPS = os.getenv('CDN_HTTPS', True) 85 | 86 | class ProductionConfig(Config): 87 | DEBUG = False 88 | 89 | # Security 90 | SESSION_COOKIE_HTTPONLY = True 91 | REMEMBER_COOKIE_HTTPONLY = True 92 | REMEMBER_COOKIE_DURATION = 3600 93 | 94 | class DebugConfig(Config): 95 | DEBUG = True 96 | 97 | # Load all possible configurations 98 | config_dict = { 99 | 'Production': ProductionConfig, 100 | 'Debug' : DebugConfig 101 | } 102 | -------------------------------------------------------------------------------- /apps/dyn_dt/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | """ 3 | Copyright (c) 2019 - present AppSeed.us 4 | """ 5 | 6 | from flask import Blueprint 7 | 8 | blueprint = Blueprint( 9 | 'table_blueprint', 10 | __name__, 11 | url_prefix='' 12 | ) 13 | -------------------------------------------------------------------------------- /apps/dyn_dt/utils.py: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | """ 3 | Copyright (c) 2019 - present AppSeed.us 4 | """ 5 | 6 | import importlib 7 | from sqlalchemy import or_ 8 | from sqlalchemy import DateTime, func 9 | from apps import db 10 | 11 | class PageItems(db.Model): 12 | __tablename__ = 'page_items' 13 | id = db.Column(db.Integer, primary_key=True) 14 | parent = db.Column(db.String(255), nullable=True) 15 | items_per_page = db.Column(db.Integer, default=25) 16 | 17 | 18 | class HideShowFilter(db.Model): 19 | __tablename__ = 'hide_show_filter' 20 | id = db.Column(db.Integer, primary_key=True) 21 | parent = db.Column(db.String(255), nullable=True) 22 | key = db.Column(db.String(255), nullable=False) 23 | value = db.Column(db.Boolean, default=False) 24 | 25 | 26 | class ModelFilter(db.Model): 27 | __tablename__ = 'model_filter' 28 | id = db.Column(db.Integer, primary_key=True) 29 | parent = db.Column(db.String(255), nullable=True) 30 | key = db.Column(db.String(255), nullable=False) 31 | value = db.Column(db.String(255), nullable=False) 32 | 33 | 34 | def get_model_fk_values(aModelClass): 35 | fk_values = {} 36 | 37 | current_table_name = aModelClass.__tablename__ 38 | 39 | for relationship in aModelClass.__mapper__.relationships: 40 | if relationship.direction.name == 'MANYTOONE': 41 | related_model = relationship.mapper.class_ 42 | foreign_key_column = list(relationship.local_columns)[0] 43 | referenced_table_name = list(foreign_key_column.foreign_keys)[0].column.table.name 44 | 45 | if referenced_table_name != current_table_name: 46 | field_name = relationship.key 47 | related_instances = related_model.query.all() 48 | fk_values[field_name] = related_instances 49 | 50 | return fk_values 51 | 52 | 53 | def get_model_field_names(model, field_type): 54 | """Returns a list of field names based on the given field type in SQLAlchemy.""" 55 | return [ 56 | column.name for column in model.__table__.columns 57 | if isinstance(column.type, field_type) 58 | ] 59 | 60 | 61 | def name_to_class(name: str): 62 | try: 63 | module_name = '.'.join(name.split('.')[:-1]) 64 | class_name = name.split('.')[-1] 65 | 66 | module = importlib.import_module(module_name) 67 | return getattr(module, class_name) 68 | except Exception as e: 69 | print(f"Error importing {name}: {e}") 70 | return None 71 | 72 | 73 | def user_filter(request, query, fields, fk_fields=[]): 74 | value = request.args.get('search') 75 | 76 | if value: 77 | dynamic_filter = [] 78 | 79 | for field in fields: 80 | if field not in fk_fields: 81 | dynamic_filter.append(getattr(query.column_descriptions[0]['entity'], field).ilike(f"%{value}%")) 82 | 83 | query = query.filter(or_(*dynamic_filter)) 84 | 85 | return query 86 | 87 | 88 | def exclude_auto_gen_fields(aModelClass): 89 | exclude_fields = [ 90 | field.name for field in aModelClass.__table__.columns 91 | if isinstance(field.type, DateTime) and ( 92 | field.default is not None or 93 | field.server_default is not None or 94 | field.onupdate is not None or 95 | isinstance(field.default, func) or 96 | isinstance(field.onupdate, func) 97 | ) 98 | ] 99 | return exclude_fields 100 | -------------------------------------------------------------------------------- /apps/exceptions/exception.py: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | """ 3 | Copyright (c) 2019 - present AppSeed.us 4 | """ 5 | 6 | class InvalidUsage(Exception): 7 | status_code = 400 8 | 9 | def __init__(self, message, status_code=None, payload=None): 10 | Exception.__init__(self) 11 | self.message = message 12 | if status_code is not None: 13 | self.status_code = status_code 14 | self.payload = payload 15 | 16 | def to_dict(self): 17 | rv = dict(self.payload or ()) 18 | rv['message'] = self.message 19 | 20 | return rv -------------------------------------------------------------------------------- /apps/home/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | """ 3 | Copyright (c) 2019 - present AppSeed.us 4 | """ 5 | 6 | from flask import Blueprint 7 | 8 | blueprint = Blueprint( 9 | 'home_blueprint', 10 | __name__, 11 | url_prefix='' 12 | ) 13 | -------------------------------------------------------------------------------- /apps/home/routes.py: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | """ 3 | Copyright (c) 2019 - present AppSeed.us 4 | """ 5 | 6 | from apps.home import blueprint 7 | from flask import render_template, request 8 | from flask_login import login_required 9 | from jinja2 import TemplateNotFound 10 | 11 | 12 | @blueprint.route('/index') 13 | @login_required 14 | def index(): 15 | 16 | return render_template('home/index.html', segment='index') 17 | 18 | 19 | @blueprint.route('/