Repository: app-generator/flask-volt-dashboard Branch: master Commit: 857c61d65435 Files: 293 Total size: 1.6 MB Directory structure: gitextract_h_druol2/ ├── .dockerignore ├── .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 │ └── models.py ├── build.sh ├── docker-compose.yml ├── env.sample ├── gunicorn-cfg.py ├── nginx/ │ └── appseed-app.conf ├── package.json ├── postcss.config.js ├── render.yaml ├── requirements.txt ├── run.py ├── static/ │ └── assets/ │ ├── .gitkeep │ ├── css/ │ │ └── volt.css │ ├── gulpfile.js │ ├── img/ │ │ └── favicon/ │ │ ├── browserconfig.xml │ │ ├── manifest.json │ │ └── site.webmanifest │ ├── js/ │ │ └── volt.js │ ├── package.json │ ├── scss/ │ │ ├── custom/ │ │ │ └── _variables.scss │ │ ├── volt/ │ │ │ ├── _components.scss │ │ │ ├── _forms.scss │ │ │ ├── _functions.scss │ │ │ ├── _layout.scss │ │ │ ├── _mixins.scss │ │ │ ├── _utilities.scss │ │ │ ├── _variables.scss │ │ │ ├── _vendor.scss │ │ │ ├── components/ │ │ │ │ ├── _accordions.scss │ │ │ │ ├── _alerts.scss │ │ │ │ ├── _animations.scss │ │ │ │ ├── _avatars.scss │ │ │ │ ├── _badge.scss │ │ │ │ ├── _body.scss │ │ │ │ ├── _breadcrumb.scss │ │ │ │ ├── _buttons.scss │ │ │ │ ├── _card.scss │ │ │ │ ├── _carousel.scss │ │ │ │ ├── _charts.scss │ │ │ │ ├── _close.scss │ │ │ │ ├── _custom-forms.scss │ │ │ │ ├── _datepicker.scss │ │ │ │ ├── _dropdown.scss │ │ │ │ ├── _icons.scss │ │ │ │ ├── _images.scss │ │ │ │ ├── _list-group.scss │ │ │ │ ├── _modal.scss │ │ │ │ ├── _nav.scss │ │ │ │ ├── _pagination.scss │ │ │ │ ├── _popover.scss │ │ │ │ ├── _progress.scss │ │ │ │ ├── _scrollbar.scss │ │ │ │ ├── _shapes.scss │ │ │ │ ├── _steps.scss │ │ │ │ ├── _tables.scss │ │ │ │ ├── _timelines.scss │ │ │ │ ├── _tooltip.scss │ │ │ │ └── _type.scss │ │ │ ├── forms/ │ │ │ │ ├── _form-check.scss │ │ │ │ ├── _form-control.scss │ │ │ │ ├── _form-select.scss │ │ │ │ └── _input-group.scss │ │ │ ├── layout/ │ │ │ │ ├── _footer.scss │ │ │ │ ├── _navbar.scss │ │ │ │ ├── _section.scss │ │ │ │ ├── _sidebar.scss │ │ │ │ └── _sidenav.scss │ │ │ └── mixins/ │ │ │ ├── _animations.scss │ │ │ ├── _background-variant.scss │ │ │ ├── _icon.scss │ │ │ ├── _modals.scss │ │ │ ├── _popover.scss │ │ │ ├── _transform.scss │ │ │ └── _utilities.scss │ │ └── volt.scss │ └── vendor/ │ ├── @popperjs/ │ │ └── core/ │ │ └── dist/ │ │ ├── cjs/ │ │ │ ├── popper-base.js │ │ │ ├── popper-base.js.flow │ │ │ ├── popper-lite.js │ │ │ ├── popper-lite.js.flow │ │ │ ├── popper.js │ │ │ └── popper.js.flow │ │ ├── esm/ │ │ │ ├── createPopper.js │ │ │ ├── dom-utils/ │ │ │ │ ├── contains.js │ │ │ │ ├── getBoundingClientRect.js │ │ │ │ ├── getClippingRect.js │ │ │ │ ├── getCompositeRect.js │ │ │ │ ├── getComputedStyle.js │ │ │ │ ├── getDocumentElement.js │ │ │ │ ├── getDocumentRect.js │ │ │ │ ├── getHTMLElementScroll.js │ │ │ │ ├── getLayoutRect.js │ │ │ │ ├── getNodeName.js │ │ │ │ ├── getNodeScroll.js │ │ │ │ ├── getOffsetParent.js │ │ │ │ ├── getParentNode.js │ │ │ │ ├── getScrollParent.js │ │ │ │ ├── getViewportRect.js │ │ │ │ ├── getWindow.js │ │ │ │ ├── getWindowScroll.js │ │ │ │ ├── getWindowScrollBarX.js │ │ │ │ ├── instanceOf.js │ │ │ │ ├── isScrollParent.js │ │ │ │ ├── isTableElement.js │ │ │ │ └── listScrollParents.js │ │ │ ├── enums.js │ │ │ ├── index.js │ │ │ ├── modifiers/ │ │ │ │ ├── applyStyles.js │ │ │ │ ├── arrow.js │ │ │ │ ├── computeStyles.js │ │ │ │ ├── eventListeners.js │ │ │ │ ├── flip.js │ │ │ │ ├── hide.js │ │ │ │ ├── index.js │ │ │ │ ├── offset.js │ │ │ │ ├── popperOffsets.js │ │ │ │ └── preventOverflow.js │ │ │ ├── popper-base.js │ │ │ ├── popper-lite.js │ │ │ ├── popper.js │ │ │ ├── types.js │ │ │ └── utils/ │ │ │ ├── computeAutoPlacement.js │ │ │ ├── computeOffsets.js │ │ │ ├── debounce.js │ │ │ ├── detectOverflow.js │ │ │ ├── expandToHashMap.js │ │ │ ├── format.js │ │ │ ├── getAltAxis.js │ │ │ ├── getAltLen.js │ │ │ ├── getBasePlacement.js │ │ │ ├── getFreshSideObject.js │ │ │ ├── getMainAxisFromPlacement.js │ │ │ ├── getOppositePlacement.js │ │ │ ├── getOppositeVariationPlacement.js │ │ │ ├── getVariation.js │ │ │ ├── math.js │ │ │ ├── mergeByName.js │ │ │ ├── mergePaddingObject.js │ │ │ ├── orderModifiers.js │ │ │ ├── rectToClientRect.js │ │ │ ├── uniqueBy.js │ │ │ ├── validateModifiers.js │ │ │ └── within.js │ │ └── umd/ │ │ ├── popper-base.min.js.flow │ │ ├── popper-lite.min.js.flow │ │ └── popper.min.js.flow │ ├── chartist-plugin-tooltips/ │ │ └── dist/ │ │ └── chartist-plugin-tooltip.css │ ├── notyf/ │ │ ├── notyf.es.js │ │ └── notyf.umd.js │ ├── nouislider/ │ │ └── distribute/ │ │ ├── nouislider.min.mjs │ │ └── nouislider.mjs │ ├── onscreen/ │ │ └── dist/ │ │ ├── index.js │ │ └── on-screen.es6.js │ ├── simplebar/ │ │ └── dist/ │ │ ├── simplebar-core.esm.js │ │ ├── simplebar.esm.js │ │ └── simplebar.umd.js │ ├── vanillajs-datepicker/ │ │ └── dist/ │ │ └── js/ │ │ └── locales/ │ │ ├── ar-tn.js │ │ ├── ar.js │ │ ├── az.js │ │ ├── bg.js │ │ ├── bm.js │ │ ├── bn.js │ │ ├── br.js │ │ ├── bs.js │ │ ├── ca.js │ │ ├── cs.js │ │ ├── cy.js │ │ ├── da.js │ │ ├── de.js │ │ ├── el.js │ │ ├── en-AU.js │ │ ├── en-CA.js │ │ ├── en-GB.js │ │ ├── en-IE.js │ │ ├── en-NZ.js │ │ ├── en-ZA.js │ │ ├── eo.js │ │ ├── es.js │ │ ├── et.js │ │ ├── eu.js │ │ ├── fa.js │ │ ├── fi.js │ │ ├── fo.js │ │ ├── fr-CH.js │ │ ├── fr.js │ │ ├── gl.js │ │ ├── he.js │ │ ├── hi.js │ │ ├── hr.js │ │ ├── hu.js │ │ ├── hy.js │ │ ├── id.js │ │ ├── is.js │ │ ├── it-CH.js │ │ ├── it.js │ │ ├── ja.js │ │ ├── ka.js │ │ ├── kk.js │ │ ├── km.js │ │ ├── ko.js │ │ ├── lt.js │ │ ├── lv.js │ │ ├── me.js │ │ ├── mk.js │ │ ├── mn.js │ │ ├── mr.js │ │ ├── ms.js │ │ ├── nl-BE.js │ │ ├── nl.js │ │ ├── no.js │ │ ├── oc.js │ │ ├── pl.js │ │ ├── pt-BR.js │ │ ├── pt.js │ │ ├── ro.js │ │ ├── ru.js │ │ ├── si.js │ │ ├── sk.js │ │ ├── sl.js │ │ ├── sq.js │ │ ├── sr-latn.js │ │ ├── sr.js │ │ ├── sv.js │ │ ├── sw.js │ │ ├── ta.js │ │ ├── tg.js │ │ ├── th.js │ │ ├── tk.js │ │ ├── tr.js │ │ ├── uk.js │ │ ├── uz-cyrl.js │ │ ├── uz-latn.js │ │ ├── vi.js │ │ ├── zh-CN.js │ │ └── zh-TW.js │ └── waypoints/ │ ├── lib/ │ │ └── waypoints.debug.js │ ├── licenses.txt │ └── testem.json ├── templates/ │ ├── .gitkeep │ ├── accounts/ │ │ ├── login.html │ │ └── register.html │ ├── charts/ │ │ └── index.html │ ├── dyn_dt/ │ │ ├── index.html │ │ └── model.html │ ├── home/ │ │ ├── components-buttons.html │ │ ├── components-forms.html │ │ ├── components-modals.html │ │ ├── components-notifications.html │ │ ├── components-typography.html │ │ ├── dashboard.html │ │ ├── index.html │ │ ├── page-403.html │ │ ├── page-404.html │ │ ├── page-500.html │ │ ├── page-forgot-password.html │ │ ├── page-lock.html │ │ ├── page-reset-password.html │ │ ├── page-sign-in.html │ │ ├── page-sign-up.html │ │ ├── settings.html │ │ ├── tables-bootstrap-tables.html │ │ └── transactions.html │ ├── includes/ │ │ ├── footer.html │ │ ├── items-table.html │ │ ├── navigation.html │ │ ├── scripts.html │ │ ├── settings-box.html │ │ └── sidebar.html │ └── layouts/ │ ├── base-fullscreen.html │ └── base.html └── vite.config.js ================================================ FILE CONTENTS ================================================ ================================================ FILE: .dockerignore ================================================ .git __pycache__ *.pyc *.pyo *.pyd ================================================ FILE: .gitignore ================================================ # byte-compiled / optimized / DLL files __pycache__/ *.py[cod] # tests and coverage *.pytest_cache .coverage # database & logs *.db *.sqlite3 *.log # venv env venv # other .DS_Store # sphinx docs _build _static _templates # javascript package-lock.json .vscode/symbols.json apps/static/assets/node_modules apps/static/assets/yarn.lock apps/static/assets/.temp migrations README_bk.md ================================================ FILE: CHANGELOG.md ================================================ # Change Log ## [1.0.17] 2025-04-01 ### Changes - Update RM (minor) ## [1.0.16] 2024-05-18 ### Changes - Updated DOCS (readme) - [Custom Development](https://appseed.us/custom-development/) Section - [CI/CD Assistance for AWS, DO](https://appseed.us/terms/#section-ci-cd) ## [1.0.15] 2024-03-06 ### Changes - Update [Custom Development](https://appseed.us/custom-development/) Section - New Pricing: `$3,999` ## [1.0.14] 2023-10-07 ### Changes - Update Dependencies - Silent fallback to SQLite ## [1.0.13] 2023-06-22 ### Changes - Print UserID on `index` - Via export in controller - Via `current_user` in view ## [1.0.12] 2023-03-15 ### Changes - DOCS Update - [Volt Dashboard Flask](https://docs.appseed.us/products/flask-dashboards/volt/) - `official help` - Links Curation (minor) ## [1.0.11] 2022-12-31 ### Changes - Added page compression for PRODUCTION env - `DEBUG=False` ## [1.0.10] 2022-12-31 ### Changes - Deployment-ready for Render (CI/CD) - `render.yaml` - `build.sh` ## [1.0.9] 2022-12-31 ### Changes - `DB Management` Improvement - `Silent fallback` to **SQLite** ## [1.0.8] 2022-09-07 ### Improvements - Added OAuth via Github - Improved Auth Pages ## [1.0.7] 2022-05-25 ### Improvements - Built with [Volt Dashboard Generator](https://appseed.us/generator/volt-dashboard/) - Timestamp: `2022-05-25 22:26` - Codebase refactoring - Added CDN Support - via `.env` **ASSETS_ROOT** ## [1.0.6] 2022-01-16 ### Improvements - Bump Flask Codebase to [v2stable.0.1](https://github.com/app-generator/boilerplate-code-flask-dashboard/releases) - Dependencies update (all packages) - Flask==2.0.2 (latest stable version) - flask_wtf==1.0.0 - jinja2==3.0.3 - flask-restx==0.5.1 - Forms Update: - Replace `TextField` (deprecated) with `StringField` ## Unreleased ### Fixes - 2021-11-08 - `v1.0.6-rc1` - ImportError: cannot import name 'TextField' from 'wtforms' - Problem caused by `WTForms-3.0.0` - Fix: use **WTForms==2.3.3** ## [1.0.5] 2021-09-16 ### Improvements & Fixes - Bump Flask Codebase to [v2.0.0](https://github.com/app-generator/boilerplate-code-flask-dashboard/releases) - Dependencies update (all packages) - Use Flask==2.0.1 (latest stable version) - Better Code formatting - Improved Files organization - Optimize imports - Docker Scripts Update ## [1.0.4] 2021-08-27 ### Improvements - Bump UI - [Volt Dashboard v1.4.1](https://github.com/themesberg/volt-bootstrap-5-dashboard/releases) ## Unreleased 2021-05-26 ### Tooling - Added scripts to recompile the SCSS files - `app/base/static/assets/` - gulpfile.js - `app/base/static/assets/` - package.json - `Update README` - [Recompile SCSS](https://github.com/app-generator/flask-dashboard-volt#recompile-css) (new section) ## [1.0.3] 2021-05-16 ### Dependencies Update - Bump Codebase: [Flask Dashboard](https://github.com/app-generator/boilerplate-code-flask-dashboard) v1.0.6 - Freeze used versions in `requirements.txt` - jinja2 = 2.11.3 ## [1.0.2] 2021-03-30 ### Improvements - Bump UI: [Jinja Volt](https://github.com/app-generator/jinja-volt-dashboard/releases) v1.0.1 - [Volt Dashboard](https://github.com/themesberg/volt-bootstrap-5-dashboard/releases) v1.3.2 ## [1.0.1] 2021-03-18 ### Improvements - Bump Codebase: [Flask Dashboard](https://github.com/app-generator/boilerplate-code-flask-dashboard) v1.0.5 - Freeze used versions in `requirements.txt` - flask_sqlalchemy = 2.4.4 - sqlalchemy = 1.3.23 ## [1.0.0] 2021-01-17 - Bump UI: [Jinja Volt](https://github.com/app-generator/jinja-volt-dashboard/releases) v1.0.0 - [Volt Dashboard](https://github.com/themesberg/volt-bootstrap-5-dashboard/releases/tag) v1.2.0 - Codebase: [Flask Dashboard](https://github.com/app-generator/boilerplate-code-flask-dashboard/releases) v1.0.3 ================================================ FILE: Dockerfile ================================================ FROM python:3.10 # set environment variables ENV PYTHONDONTWRITEBYTECODE 1 ENV PYTHONUNBUFFERED 1 ENV FLASK_APP run.py ENV DEBUG True COPY requirements.txt . # install python dependencies RUN pip install --upgrade pip RUN pip install --no-cache-dir -r requirements.txt COPY env.sample .env COPY . . RUN flask db init RUN flask db migrate RUN flask db upgrade # gunicorn CMD ["gunicorn", "--config", "gunicorn-cfg.py", "run:app"] ================================================ FILE: LICENSE.md ================================================ # MIT License Copyright (c) 2019 - present [AppSeed](http://appseed.us/)
## Licensing Information
| Item | - | | ---------------------------------- | --- | | License Type | MIT | | Use for print | **YES** | | Create single personal website/app | **YES** | | Create single website/app for client | **YES** | | Create multiple website/apps for clients | **YES** | | Create multiple SaaS applications | **YES** | | End-product paying users | **YES** | | Product sale | **YES** | | Remove footer credits | **YES** | | --- | --- | | Remove copyright mentions from source code | NO | | Production deployment assistance | NO | | Create HTML/CSS template for sale | NO | | Create Theme/Template for CMS for sale | NO | | Separate sale of our UI Elements | NO |
--- For more information regarding licensing, please contact the AppSeed Service < *support@appseed.us* > ================================================ FILE: README.md ================================================ # [Flask Volt Dashboard](https://app-generator.dev/product/volt-dashboard/flask/) Open-source **Flask Starter with Volt Dashboard Design**, an open-source iconic `Bootstrap` design. The product is designed to deliver the best possible user experience with highly customizable feature-rich pages. - 👉 [Flask Volt Dashboard](https://app-generator.dev/product/volt-dashboard/flask/) - `Product Page` - 👉 [Flask Volt Dashboard](https://flask-volt-dashboard.appseed-srv1.com/) - `LIVE Demo` - 👉 [Flask Volt Dashboard](https://app-generator.dev/docs/products/flask/volt-dashboard/index.html) - `Complete Information` and Support Links - [Getting Started with Flask](https://app-generator.dev/docs/technologies/flask/index.html) - a `comprehensive tutorial` - `Configuration`: Install Tailwind/Flowbite, Prepare Environment, Setting up the Database - `Start with Docker` - `Manual Build` - `Start the project` - `Deploy on Render`
## `PROMO` Domains for sale - 👉 **[AppSeed.us - $49k](https://appseed.us/)** - 👉 **[Admin-Dashboards.com - $15k](https://www.admin-dashboards.com/)** - 👉 **[UI-Themes.com - $5k](https://ui-themes.com/)** - 👉 **[Simpllo.com - $10k](https://www.simpllo.com/)** - 👉 **[123SiteBuilder.net - $5k](https://www.123sitebuilder.net/)**
## Features - Simple, Easy-to-Extend codebase, [Blueprint Pattern](https://app-generator.dev/blog/flask-blueprints-a-developers-guide/) - Up-to-date Dependencies - [Volt Dashboard](https://app-generator.dev/docs/templates/bootstrap/volt-dashboard.html) Full Integration - [Bootstrap](https://app-generator.dev/docs/templates/bootstrap/index.html) 5 Styling - Auth: Session Based, GitHub, Google - Celery Beat - DB Persistence: SQLite (default), - Easy switch to MySql/MariaDB, PgSql - Dynamic DataTables - manage data without coding - CI/CD integration for [Render](https://app-generator.dev/docs/deployment/render/index.html) - Deployment: Docker, Flask-Minify ![Volt Dashboard - Full-Stack Starter generated by AppSeed.](https://user-images.githubusercontent.com/51070104/168843604-b026fd94-5969-4be7-81ac-5887cf0958e5.png)
## Deploy LIVE > One-click deploy (requires already having an account). [![Deploy to Render](https://render.com/images/deploy-to-render-button.svg)](https://render.com/deploy)
## [Volt Dashboard PRO Version](https://app-generator.dev/product/volt-dashboard-pro/flask/) > The premium version provides more features, priority on support, and is more often updated - [Live Demo](https://flask-volt-dashboard-enh.appseed-srv1.com/). - **Simple, Easy-to-Extend** Codebase - **Volt Dashboard** Design - PRO Version - Bootstrap 4 CSS - **Deployment-Ready** for Render ![Volt Dashboard PRO - Starter generated by AppSeed.](https://user-images.githubusercontent.com/51070104/172672843-8c40a801-3438-4e9c-86db-38a34191fbdf.png)
## `Customize` with [Flask Generator](https://app-generator.dev/tools/flask-generator/) - Access the [Flask Generator](https://app-generator.dev/tools/flask-generator/) - Select the preferred design - (Optional) Design Database: edit models and fields - (Optional) Edit the fields for the extended user model - (Optional) Enable OAuth for GitHub - (Optional) Add Celery (async tasks) - (Optional) Enable Dynamic Tables Module - Docker Scripts - Render CI/Cd Scripts **The generated Flask project is available as a ZIP Archive and also uploaded to GitHub.** ![Flask Generator - Flask App Generator - User Interface for choosing the Design](https://github.com/user-attachments/assets/fbf73fc0-e9a1-4f01-86a8-aa8be55413b5) ![Flask App Generator - User Interface for Edit the Extended User Model](https://github.com/user-attachments/assets/138b9816-4f2e-454f-84f2-7409969b8548)
--- [Flask Volt Dashboard](https://app-generator.dev/product/volt-dashboard/flask/) - Open-Source **Flask** Starter provided by [App Generator](https://app-generator.dev) ================================================ FILE: apps/__init__.py ================================================ # -*- encoding: utf-8 -*- """ Copyright (c) 2019 - present AppSeed.us """ import os from flask import Flask from flask_login import LoginManager from flask_sqlalchemy import SQLAlchemy from importlib import import_module db = SQLAlchemy() login_manager = LoginManager() def register_extensions(app): db.init_app(app) login_manager.init_app(app) def register_blueprints(app): for module_name in ('authentication', 'home', 'dyn_dt', 'charts', ): module = import_module('apps.{}.routes'.format(module_name)) app.register_blueprint(module.blueprint) from apps.authentication.oauth import github_blueprint, google_blueprint def create_app(config): # Contextual static_prefix = '/static' templates_dir = os.path.dirname(config.BASE_DIR) TEMPLATES_FOLDER = os.path.join(templates_dir,'templates') STATIC_FOLDER = os.path.join(templates_dir,'static') print(' > TEMPLATES_FOLDER: ' + TEMPLATES_FOLDER) print(' > STATIC_FOLDER: ' + STATIC_FOLDER) app = Flask(__name__, static_url_path=static_prefix, template_folder=TEMPLATES_FOLDER, static_folder=STATIC_FOLDER) app.config.from_object(config) register_extensions(app) register_blueprints(app) app.register_blueprint(github_blueprint, url_prefix="/login") app.register_blueprint(google_blueprint, url_prefix="/login") return app ================================================ FILE: apps/authentication/__init__.py ================================================ # -*- encoding: utf-8 -*- """ Copyright (c) 2019 - present AppSeed.us """ from flask import Blueprint blueprint = Blueprint( 'authentication_blueprint', __name__, url_prefix='' ) ================================================ FILE: apps/authentication/forms.py ================================================ # -*- encoding: utf-8 -*- """ Copyright (c) 2019 - present AppSeed.us """ from flask_wtf import FlaskForm from wtforms import StringField, PasswordField from wtforms.validators import Email, DataRequired # login and registration class LoginForm(FlaskForm): username = StringField('Username', id='username_login', validators=[DataRequired()]) password = PasswordField('Password', id='pwd_login', validators=[DataRequired()]) class CreateAccountForm(FlaskForm): username = StringField('Username', id='username_create', validators=[DataRequired()]) email = StringField('Email', id='email_create', validators=[DataRequired(), Email()]) password = PasswordField('Password', id='pwd_create', validators=[DataRequired()]) ================================================ FILE: apps/authentication/models.py ================================================ # -*- encoding: utf-8 -*- """ Copyright (c) 2019 - present AppSeed.us """ from flask_login import UserMixin from sqlalchemy.exc import SQLAlchemyError, IntegrityError from flask_dance.consumer.storage.sqla import OAuthConsumerMixin from apps import db, login_manager from apps.authentication.util import hash_pass class Users(db.Model, UserMixin): __tablename__ = 'users' id = db.Column(db.Integer, primary_key=True) username = db.Column(db.String(64), unique=True) email = db.Column(db.String(64), unique=True) password = db.Column(db.LargeBinary) bio = db.Column(db.Text(), nullable=True) oauth_github = db.Column(db.String(100), nullable=True) oauth_google = db.Column(db.String(100), nullable=True) readonly_fields = ["id", "username", "email", "oauth_github", "oauth_google"] def __init__(self, **kwargs): for property, value in kwargs.items(): # depending on whether value is an iterable or not, we must # unpack it's value (when **kwargs is request.form, some values # will be a 1-element list) if hasattr(value, '__iter__') and not isinstance(value, str): # the ,= unpack of a singleton fails PEP8 (travis flake8 test) value = value[0] if property == 'password': value = hash_pass(value) # we need bytes here (not plain str) setattr(self, property, value) def __repr__(self): return str(self.username) @classmethod def find_by_email(cls, email: str) -> "Users": return cls.query.filter_by(email=email).first() @classmethod def find_by_username(cls, username: str) -> "Users": return cls.query.filter_by(username=username).first() @classmethod def find_by_id(cls, _id: int) -> "Users": return cls.query.filter_by(id=_id).first() def save(self) -> None: try: db.session.add(self) db.session.commit() except SQLAlchemyError as e: db.session.rollback() db.session.close() error = str(e.__dict__['orig']) raise IntegrityError(error, 422) def delete_from_db(self) -> None: try: db.session.delete(self) db.session.commit() except SQLAlchemyError as e: db.session.rollback() db.session.close() error = str(e.__dict__['orig']) raise IntegrityError(error, 422) return @login_manager.user_loader def user_loader(id): return Users.query.filter_by(id=id).first() @login_manager.request_loader def request_loader(request): username = request.form.get('username') user = Users.query.filter_by(username=username).first() return user if user else None class OAuth(OAuthConsumerMixin, db.Model): user_id = db.Column(db.Integer, db.ForeignKey("users.id", ondelete="cascade"), nullable=False) user = db.relationship(Users) ================================================ FILE: apps/authentication/oauth.py ================================================ # -*- encoding: utf-8 -*- """ Copyright (c) 2019 - present AppSeed.us """ import os from flask import current_app as app from flask_login import current_user, login_user from flask_dance.consumer import oauth_authorized from flask_dance.contrib.github import github, make_github_blueprint from flask_dance.contrib.google import google, make_google_blueprint from flask_dance.consumer.storage.sqla import SQLAlchemyStorage from sqlalchemy.orm.exc import NoResultFound from apps.config import Config from .models import Users, db, OAuth from flask import redirect, url_for from flask import flash github_blueprint = make_github_blueprint( client_id=Config.GITHUB_ID, client_secret=Config.GITHUB_SECRET, scope = 'user', storage=SQLAlchemyStorage( OAuth, db.session, user=current_user, user_required=False, ), ) @oauth_authorized.connect_via(github_blueprint) def github_logged_in(blueprint, token): info = github.get("/user") if info.ok: account_info = info.json() username = account_info["login"] query = Users.query.filter_by(oauth_github=username) try: user = query.one() login_user(user) except NoResultFound: # Save to db user = Users() user.username = '(gh)' + username user.oauth_github = username # Save current user db.session.add(user) db.session.commit() login_user(user) # Google google_blueprint = make_google_blueprint( client_id=Config.GOOGLE_ID, client_secret=Config.GOOGLE_SECRET, scope=[ "openid", "https://www.googleapis.com/auth/userinfo.email", "https://www.googleapis.com/auth/userinfo.profile", ], storage=SQLAlchemyStorage( OAuth, db.session, user=current_user, user_required=False, ), ) @oauth_authorized.connect_via(google_blueprint) def google_logged_in(blueprint, token): info = google.get("/oauth2/v1/userinfo") if info.ok: account_info = info.json() username = account_info["given_name"] email = account_info["email"] query = Users.query.filter_by(oauth_google=username) try: user = query.one() login_user(user) except NoResultFound: # Save to db user = Users() user.username = '(google)' + username user.oauth_google = username user.email = email # Save current user db.session.add(user) db.session.commit() login_user(user) ================================================ FILE: apps/authentication/routes.py ================================================ # -*- encoding: utf-8 -*- """ Copyright (c) 2019 - present AppSeed.us """ from flask import render_template, redirect, request, url_for from flask_login import ( current_user, login_user, logout_user ) from flask_dance.contrib.github import github from flask_dance.contrib.google import google from apps import db, login_manager from apps.authentication import blueprint from apps.authentication.forms import LoginForm, CreateAccountForm from apps.authentication.models import Users from apps.config import Config from apps.authentication.util import verify_pass @blueprint.route('/') def route_default(): return redirect(url_for('authentication_blueprint.login')) # Login & Registration @blueprint.route("/github") def login_github(): """ Github login """ if not github.authorized: return redirect(url_for("github.login")) res = github.get("/user") return redirect(url_for('home_blueprint.index')) @blueprint.route("/google") def login_google(): """ Google login """ if not google.authorized: return redirect(url_for("google.login")) res = google.get("/oauth2/v1/userinfo") return redirect(url_for('home_blueprint.index')) @blueprint.route('/login', methods=['GET', 'POST']) def login(): login_form = LoginForm(request.form) if 'login' in request.form: # read form data username = request.form['username'] password = request.form['password'] # Locate user user = Users.query.filter_by(username=username).first() # Check the password if user and verify_pass(password, user.password): login_user(user) return redirect(url_for('authentication_blueprint.route_default')) # Something (user or pass) is not ok return render_template('accounts/login.html', msg='Wrong user or password', form=login_form) if not current_user.is_authenticated: return render_template('accounts/login.html', form=login_form) return redirect(url_for('home_blueprint.index')) @blueprint.route('/register', methods=['GET', 'POST']) def register(): create_account_form = CreateAccountForm(request.form) if 'register' in request.form: username = request.form['username'] email = request.form['email'] # Check usename exists user = Users.query.filter_by(username=username).first() if user: return render_template('accounts/register.html', msg='Username already registered', success=False, form=create_account_form) # Check email exists user = Users.query.filter_by(email=email).first() if user: return render_template('accounts/register.html', msg='Email already registered', success=False, form=create_account_form) # else we can create the user user = Users(**request.form) db.session.add(user) db.session.commit() # Delete user from session logout_user() return render_template('accounts/register.html', msg='Account created successfully.', success=True, form=create_account_form) else: return render_template('accounts/register.html', form=create_account_form) @blueprint.route('/logout') def logout(): logout_user() return redirect(url_for('authentication_blueprint.login')) # Errors @login_manager.unauthorized_handler def unauthorized_handler(): return render_template('home/page-403.html'), 403 @blueprint.errorhandler(403) def access_forbidden(error): return render_template('home/page-403.html'), 403 @blueprint.errorhandler(404) def not_found_error(error): return render_template('home/page-404.html'), 404 @blueprint.errorhandler(500) def internal_error(error): return render_template('home/page-500.html'), 500 @blueprint.context_processor def has_github(): return {'has_github': bool(Config.GITHUB_ID) and bool(Config.GITHUB_SECRET)} @blueprint.context_processor def has_google(): return {'has_google': bool(Config.GOOGLE_ID) and bool(Config.GOOGLE_SECRET)} ================================================ FILE: apps/authentication/util.py ================================================ # -*- encoding: utf-8 -*- """ Copyright (c) 2019 - present AppSeed.us """ import os import hashlib import binascii # Inspiration -> https://www.vitoshacademy.com/hashing-passwords-in-python/ def hash_pass(password): """Hash a password for storing.""" salt = hashlib.sha256(os.urandom(60)).hexdigest().encode('ascii') pwdhash = hashlib.pbkdf2_hmac('sha512', password.encode('utf-8'), salt, 100000) pwdhash = binascii.hexlify(pwdhash) return (salt + pwdhash) # return bytes def verify_pass(provided_password, stored_password): """Verify a stored password against one provided by user""" stored_password = stored_password.decode('ascii') salt = stored_password[:64] stored_password = stored_password[64:] pwdhash = hashlib.pbkdf2_hmac('sha512', provided_password.encode('utf-8'), salt.encode('ascii'), 100000) pwdhash = binascii.hexlify(pwdhash).decode('ascii') return pwdhash == stored_password ================================================ FILE: apps/charts/__init__.py ================================================ # -*- encoding: utf-8 -*- """ Copyright (c) 2019 - present AppSeed.us """ from flask import Blueprint blueprint = Blueprint( 'charts_blueprint', __name__, url_prefix='' ) ================================================ FILE: apps/charts/routes.py ================================================ # -*- encoding: utf-8 -*- """ Copyright (c) 2019 - present AppSeed.us """ from apps.charts import blueprint from flask import render_template from apps.models import Product @blueprint.route('/charts') def charts(): products = [{'name': product.name, 'price': product.price} for product in Product.get_list()] return render_template('charts/index.html', segment='charts', products=products) ================================================ FILE: apps/config.py ================================================ # -*- encoding: utf-8 -*- """ Copyright (c) 2019 - present AppSeed.us """ import os from pathlib import Path class Config(object): BASE_DIR = Path(__file__).resolve().parent USERS_ROLES = { 'ADMIN' :1 , 'USER' : 2 } USERS_STATUS = { 'ACTIVE' :1 , 'SUSPENDED' : 2 } # celery CELERY_BROKER_URL = "redis://localhost:6379" CELERY_RESULT_BACKEND = "redis://localhost:6379" CELERY_HOSTMACHINE = "celery@app-generator" # Set up the App SECRET_KEY SECRET_KEY = os.getenv('SECRET_KEY', 'S3cret_999') # Social AUTH context SOCIAL_AUTH_GITHUB = False GITHUB_ID = os.getenv('GITHUB_ID' , None) GITHUB_SECRET = os.getenv('GITHUB_SECRET', None) # Enable/Disable Github Social Login if GITHUB_ID and GITHUB_SECRET: SOCIAL_AUTH_GITHUB = True GOOGLE_ID = os.getenv('GOOGLE_ID' , None) GOOGLE_SECRET = os.getenv('GOOGLE_SECRET', None) # Enable/Disable Google Social Login if GOOGLE_ID and GOOGLE_SECRET: SOCIAL_AUTH_GOOGLE = True SQLALCHEMY_TRACK_MODIFICATIONS = False DB_ENGINE = os.getenv('DB_ENGINE' , None) DB_USERNAME = os.getenv('DB_USERNAME' , None) DB_PASS = os.getenv('DB_PASS' , None) DB_HOST = os.getenv('DB_HOST' , None) DB_PORT = os.getenv('DB_PORT' , None) DB_NAME = os.getenv('DB_NAME' , None) USE_SQLITE = True # try to set up a Relational DBMS if DB_ENGINE and DB_NAME and DB_USERNAME: try: # Relational DBMS: PSQL, MySql SQLALCHEMY_DATABASE_URI = '{}://{}:{}@{}:{}/{}'.format( DB_ENGINE, DB_USERNAME, DB_PASS, DB_HOST, DB_PORT, DB_NAME ) USE_SQLITE = False except Exception as e: print('> Error: DBMS Exception: ' + str(e) ) print('> Fallback to SQLite ') if USE_SQLITE: # This will create a file in FOLDER SQLALCHEMY_DATABASE_URI = 'sqlite:///' + os.path.join(BASE_DIR, 'db.sqlite3') DYNAMIC_DATATB = { "products": "apps.models.Product" } CDN_DOMAIN = os.getenv('CDN_DOMAIN') CDN_HTTPS = os.getenv('CDN_HTTPS', True) class ProductionConfig(Config): DEBUG = False # Security SESSION_COOKIE_HTTPONLY = True REMEMBER_COOKIE_HTTPONLY = True REMEMBER_COOKIE_DURATION = 3600 class DebugConfig(Config): DEBUG = True # Load all possible configurations config_dict = { 'Production': ProductionConfig, 'Debug' : DebugConfig } ================================================ FILE: apps/dyn_dt/__init__.py ================================================ # -*- encoding: utf-8 -*- """ Copyright (c) 2019 - present AppSeed.us """ from flask import Blueprint blueprint = Blueprint( 'table_blueprint', __name__, url_prefix='' ) ================================================ FILE: apps/dyn_dt/routes.py ================================================ # -*- encoding: utf-8 -*- """ Copyright (c) 2019 - present AppSeed.us """ import json, csv, io from flask_login import login_required from apps.dyn_dt import blueprint from flask import render_template, request, redirect, url_for, jsonify, make_response from apps.dyn_dt.utils import get_model_field_names, get_model_fk_values, name_to_class, user_filter, exclude_auto_gen_fields from apps import db, config from apps.dyn_dt.utils import * from sqlalchemy import and_ from sqlalchemy import Integer, DateTime, String, Text from datetime import datetime @blueprint.route('/dynamic-dt') def dynamic_dt(): context = { 'routes': config.Config.DYNAMIC_DATATB.keys(), 'segment': 'dynamic_dt' } return render_template('dyn_dt/index.html', **context) @blueprint.route('/create_filter/', methods=["POST"]) def create_filter(model_name): model_name = model_name.lower() if request.method == "POST": keys = request.form.getlist('key') values = request.form.getlist('value') for key, value in zip(keys, values): filter_instance = ModelFilter.query.filter_by(parent=model_name, key=key).first() if filter_instance: filter_instance.value = value else: filter_instance = ModelFilter(parent=model_name, key=key, value=value) db.session.add(filter_instance) db.session.commit() return redirect(url_for('table_blueprint.model_dt', aPath=model_name)) @blueprint.route('/create_page_items/', methods=["POST"]) def create_page_items(model_name): model_name = model_name.lower() if request.method == 'POST': items = request.form.get('items') page_items = PageItems.query.filter_by(parent=model_name).first() if page_items: page_items.items_per_page = items else: page_items = PageItems(parent=model_name, items_per_page=items) db.session.add(page_items) db.session.commit() return redirect(url_for('table_blueprint.model_dt', aPath=model_name)) @blueprint.route('/create_hide_show_filter/', methods=["POST"]) def create_hide_show_filter(model_name): model_name = model_name.lower() if request.method == "POST": data_str = list(request.form.keys())[0] data = json.loads(data_str) filter_instance = HideShowFilter.query.filter_by(parent=model_name, key=data.get('key')).first() if filter_instance: filter_instance.value = data.get('value') else: filter_instance = HideShowFilter(parent=model_name, key=data.get('key'), value=data.get('value')) db.session.add(filter_instance) db.session.commit() return jsonify({'message': 'Model updated successfully'}) @blueprint.route('/delete_filter//', methods=["GET"]) def delete_filter(model_name, id): model_name = model_name.lower() filter_instance = ModelFilter.query.filter_by(id=id, parent=model_name).first() if filter_instance: db.session.delete(filter_instance) db.session.commit() return redirect(url_for('table_blueprint.model_dt', aPath=model_name)) return jsonify({'error': 'Filter not found'}), 404 @blueprint.route('/dynamic-dt/', methods=['GET', 'POST']) def model_dt(aPath): aModelName = None aModelClass = None if aPath in config.Config.DYNAMIC_DATATB.keys(): aModelName = config.Config.DYNAMIC_DATATB[aPath] aModelClass = name_to_class(aModelName) if not aModelClass: return f'ERR: Getting ModelClass for path: {aPath}', 404 # db_fields = [field.name for field in aModelClass.__table__.columns] db_fields = [field.name for field in aModelClass.__table__.columns if not field.foreign_keys] fk_fields = get_model_fk_values(aModelClass) db_filters = [] for f in db_fields: if f not in fk_fields.keys(): db_filters.append( f ) choices_dict = {} for column in aModelClass.__table__.columns: if isinstance(column.type, db.Enum): choices_dict[column.name] = [(choice.name, choice.value) for choice in column.type.enum_class] field_names = [] for field_name in db_fields: field = HideShowFilter.query.filter_by(parent=aPath.lower(), key=field_name).first() if field: field_names.append(field) else: field = HideShowFilter(parent=aPath.lower(), key=field_name) db.session.add(field) db.session.commit() field_names.append(field) filter_string = [] filter_instance = ModelFilter.query.filter_by(parent=aPath.lower()).all() for filter_data in filter_instance: if filter_data.key in db_fields: filter_string.append(getattr(aModelClass, filter_data.key).like(f"%{filter_data.value}%")) order_by = request.args.get('order_by', 'id') if order_by not in db_fields: order_by = 'id' queryset = aModelClass.query.filter(and_(*filter_string)).order_by(order_by) # Pagination page_items = PageItems.query.filter_by(parent=aPath.lower()).order_by(PageItems.id.desc()).first() p_items = 25 if page_items: p_items = page_items.items_per_page page = request.args.get('page', 1, type=int) queryset = user_filter(request, queryset, db_fields, fk_fields.keys()) pagination = queryset.paginate(page=page, per_page=p_items, error_out=False) items = pagination.items # Read-only and field types read_only_fields = ('id', 'user_id', 'date_created', 'date_modified', ) integer_fields = get_model_field_names(aModelClass, Integer) date_time_fields = get_model_field_names(aModelClass, DateTime) text_fields = get_model_field_names(aModelClass, Text) email_fields = [] # Context context = { 'page_title': f'Dynamic DataTable - {aPath.lower().title()}', 'link': aPath, 'field_names': field_names, 'db_field_names': db_fields, 'db_filters': db_filters, 'items': items, 'pagination': pagination, 'page_items': p_items, 'filter_instance': filter_instance, 'read_only_fields': read_only_fields, 'integer_fields': integer_fields, 'date_time_fields': date_time_fields, 'email_fields': email_fields, 'text_fields': text_fields, 'fk_fields_keys': fk_fields.keys(), 'fk_fields': fk_fields, 'segment': 'dynamic_dt', 'choices_dict': choices_dict, 'exclude_auto_gen_fields': exclude_auto_gen_fields(aModelClass) } return render_template('dyn_dt/model.html', **context) @blueprint.route('/create/', methods=["POST"]) @login_required def create(aPath): aModelClass = None if aPath in config.Config.DYNAMIC_DATATB: aModelName = config.Config.DYNAMIC_DATATB[aPath] aModelClass = name_to_class(aModelName) if not aModelClass: return ' > ERR: Getting ModelClass for path: ' + aPath if request.method == 'POST': data = {} fk_fields = get_model_fk_values(aModelClass) for attribute, value in request.form.items(): if attribute in fk_fields.keys(): table_name = None for product in fk_fields[attribute]: table_name = product.__class__.__tablename__ if table_name: model_name = config.Config.DYNAMIC_DATATB[table_name] value = name_to_class(model_name).query.filter_by(id=value).first() data[attribute] = value if value else '' new_item = aModelClass(**data) db.session.add(new_item) db.session.commit() return redirect(request.referrer) @blueprint.route('/delete//', methods=["GET"]) @login_required def delete(aPath, id): aModelClass = None if aPath in config.Config.DYNAMIC_DATATB: aModelName = config.Config.DYNAMIC_DATATB[aPath] aModelClass = name_to_class(aModelName) if not aModelClass: return ' > ERR: Getting ModelClass for path: ' + aPath item = aModelClass.query.get(id) if item: db.session.delete(item) db.session.commit() return redirect(request.referrer) @blueprint.route('/update//', methods=["POST"]) @login_required def update(aPath, id): aModelClass = None if aPath in config.Config.DYNAMIC_DATATB: aModelName = config.Config.DYNAMIC_DATATB[aPath] aModelClass = name_to_class(aModelName) if not aModelClass: return ' > ERR: Getting ModelClass for path: ' + aPath item = aModelClass.query.get(id) if not item: return 'Item not found', 404 fk_fields = get_model_fk_values(aModelClass) if request.method == 'POST': for attribute, value in request.form.items(): if hasattr(item, attribute) and getattr(item, attribute, value) is not None: if attribute in fk_fields.keys(): table_name = None for product in fk_fields[attribute]: table_name = product.__class__.__tablename__ if table_name: model_name = config.Config.DYNAMIC_DATATB[table_name] value = name_to_class(model_name).query.filter_by(id=value).first() setattr(item, attribute, value) db.session.commit() return redirect(request.referrer) @blueprint.route('/export/', methods=['GET']) def export_csv(aPath): aModelName = None aModelClass = None if aPath in config.Config.DYNAMIC_DATATB: aModelName = config.Config.DYNAMIC_DATATB[aPath] aModelClass = name_to_class(aModelName) if not aModelClass: return ' > ERR: Getting ModelClass for path: ' + aPath, 400 db_field_names = [column.name for column in aModelClass.__table__.columns] fk_fields = get_model_fk_values(aModelClass) fields = [] show_fields = HideShowFilter.query.filter_by(value=False, parent=aPath.lower()).all() for field in show_fields: if field.key in db_field_names: fields.append(field.key) else: print(f"Field {field.key} does not exist in {aModelClass} model.") output = io.StringIO() writer = csv.writer(output) writer.writerow(fields) # Filtering filter_string = {} filter_instance = ModelFilter.query.filter_by(parent=aPath.lower()).all() for filter_data in filter_instance: filter_string[f'{filter_data.key}__icontains'] = filter_data.value # Ordering order_by = request.args.get('order_by', 'id') query = aModelClass.query.filter_by(**filter_string).order_by(order_by) items = user_filter(request, query, db_field_names, fk_fields) # Write rows to CSV for item in items: row_data = [] for field in fields: try: row_data.append(getattr(item, field)) except AttributeError: row_data.append('') writer.writerow(row_data) # Prepare response with CSV content response = make_response(output.getvalue()) response.headers['Content-Type'] = 'text/csv' response.headers['Content-Disposition'] = f'attachment; filename="{aPath.lower()}.csv"' return response # Template filter @blueprint.app_template_filter('getattribute') def getattribute(value, arg): try: attr_value = getattr(value, arg) if isinstance(attr_value, datetime): return attr_value.strftime("%Y-%m-%d %H:%M:%S") return attr_value except AttributeError: return '' @blueprint.app_template_filter('getenumattribute') def getenumattribute(value, arg): try: attr_value = getattr(value, arg) return attr_value.value except AttributeError: return '' @blueprint.app_template_filter('get') def get(dict_data, key): return dict_data.get(key, []) ================================================ FILE: apps/dyn_dt/utils.py ================================================ # -*- encoding: utf-8 -*- """ Copyright (c) 2019 - present AppSeed.us """ import importlib from sqlalchemy import or_ from sqlalchemy import DateTime, func from apps import db class PageItems(db.Model): __tablename__ = 'page_items' id = db.Column(db.Integer, primary_key=True) parent = db.Column(db.String(255), nullable=True) items_per_page = db.Column(db.Integer, default=25) class HideShowFilter(db.Model): __tablename__ = 'hide_show_filter' id = db.Column(db.Integer, primary_key=True) parent = db.Column(db.String(255), nullable=True) key = db.Column(db.String(255), nullable=False) value = db.Column(db.Boolean, default=False) class ModelFilter(db.Model): __tablename__ = 'model_filter' id = db.Column(db.Integer, primary_key=True) parent = db.Column(db.String(255), nullable=True) key = db.Column(db.String(255), nullable=False) value = db.Column(db.String(255), nullable=False) def get_model_fk_values(aModelClass): fk_values = {} current_table_name = aModelClass.__tablename__ for relationship in aModelClass.__mapper__.relationships: if relationship.direction.name == 'MANYTOONE': related_model = relationship.mapper.class_ foreign_key_column = list(relationship.local_columns)[0] referenced_table_name = list(foreign_key_column.foreign_keys)[0].column.table.name if referenced_table_name != current_table_name: field_name = relationship.key related_instances = related_model.query.all() fk_values[field_name] = related_instances return fk_values def get_model_field_names(model, field_type): """Returns a list of field names based on the given field type in SQLAlchemy.""" return [ column.name for column in model.__table__.columns if isinstance(column.type, field_type) ] def name_to_class(name: str): try: module_name = '.'.join(name.split('.')[:-1]) class_name = name.split('.')[-1] module = importlib.import_module(module_name) return getattr(module, class_name) except Exception as e: print(f"Error importing {name}: {e}") return None def user_filter(request, query, fields, fk_fields=[]): value = request.args.get('search') if value: dynamic_filter = [] for field in fields: if field not in fk_fields: dynamic_filter.append(getattr(query.column_descriptions[0]['entity'], field).ilike(f"%{value}%")) query = query.filter(or_(*dynamic_filter)) return query def exclude_auto_gen_fields(aModelClass): exclude_fields = [ field.name for field in aModelClass.__table__.columns if isinstance(field.type, DateTime) and ( field.default is not None or field.server_default is not None or field.onupdate is not None or isinstance(field.default, func) or isinstance(field.onupdate, func) ) ] return exclude_fields ================================================ FILE: apps/exceptions/exception.py ================================================ # -*- encoding: utf-8 -*- """ Copyright (c) 2019 - present AppSeed.us """ class InvalidUsage(Exception): status_code = 400 def __init__(self, message, status_code=None, payload=None): Exception.__init__(self) self.message = message if status_code is not None: self.status_code = status_code self.payload = payload def to_dict(self): rv = dict(self.payload or ()) rv['message'] = self.message return rv ================================================ FILE: apps/helpers.py ================================================ # -*- encoding: utf-8 -*- """ Copyright (c) 2019 - present AppSeed.us """ import os, re, uuid from colorama import Fore, Style from apps.authentication.models import Users from apps.config import Config from marshmallow import ValidationError from apps.messages import Messages from functools import wraps from flask import request from uuid import uuid4 import datetime, time message = Messages.message Currency = Config.CURRENCY PAYMENT_TYPE = Config.PAYMENT_TYPE STATE = Config.STATE regex = re.compile(r'([A-Za-z0-9]+[.-_])*[A-Za-z0-9]+@[A-Za-z0-9-]+(\.[A-Z|a-z]{2,})+') def get_ts(): return int(time.time()) def password_validate(password): """ password validate """ msg = '' while True: if len(password) < 6: msg = "Make sure your password is at lest 6 letters" return msg elif re.search('[0-9]',password) is None: msg = "Make sure your password has a number in it" return msg elif re.search('[A-Z]',password) is None: msg = "Make sure your password has a capital letter in it" return msg else: msg = True break return True def emailValidate(email): """ validate email """ if re.fullmatch(regex, email): return True else: return False # santise file name def sanitise_fille_name(value): """ remove special char """ return value.strip().lower().replace(' ', '_').replace('(', '').replace(')', '').replace(',', '').replace('=','_').replace('-', '_').replace('#', '') def createFolder(folder_name): """ create folder for save csv """ if not os.path.exists(f'{folder_name}'): os.makedirs(f'{folder_name}') return folder_name def uniqueFileName(file_name): """ for Unique file name""" file_uuid = uuid.uuid4() IMAGE_NAME = f'{file_uuid}-{file_name}' return IMAGE_NAME def serverImageUrl(file_name): """ for Unique file name""" url = f'{FTP_IMAGE_URL}{file_name}' return url def errorColor(error): """ for terminal input error color """ print(Fore.RED + f'{error}') print(Style.RESET_ALL) return True def splitUrlGetFilename(url): """ image url split and get file name """ return url.split('/')[-1] def validateCurrency(currency): """ check currency """ # if check currency validate or not if currency not in list(Currency.keys()): raise ValidationError( f"{message['invalid_currency']}, expected {','.join(Currency.keys())}", 422) def validatePaymentMethod(payment): """ check valid payment methods """ # if check PAYMENT_TYPE validate or not if payment not in list(PAYMENT_TYPE.keys()): raise ValidationError( f"{message['invalid_payment_method']}, expected {expectedValue(PAYMENT_TYPE)}", 422) else: value = 0 if payment == "cc": value = 1 elif payment == "paypal": value = 2 else: value = 3 return value def validateState(state): """ check valid state methods """ # if check state validate or not if state not in list(STATE.keys()): raise ValidationError( f"{message['invalid_state']}, expected {expectedValue(STATE)}", 422) else: value = 0 if state == "completed": value = 1 elif state == "pending": value = 2 else: value = 3 return value def expectedValue(data): """ key get values """ values = [] for k,v in data.items(): values.append(f'{v}.({k})') return ",".join(values) def createAccessToken(): """ create access token w""" rand_token = uuid4() return f"{str(rand_token)}" # token validate def token_required(f): """ check token """ @wraps(f) def decorated(*args, **kwargs): token = None if "Authorization" in request.headers: token = request.headers["Authorization"] if not token: return { "message": "Authentication Token is missing!", "error": "Unauthorized" }, 401 try: current_user = Users.find_by_api_token(token) if current_user is None: return { "message": "Invalid Authentication token!", "error": "Unauthorized" }, 401 # if not current_user["active"]: # abort(403) except Exception as e: return { "message": "Something went wrong", "error": str(e) }, 500 return f(current_user, **kwargs) return decorated ================================================ FILE: apps/home/__init__.py ================================================ # -*- encoding: utf-8 -*- """ Copyright (c) 2019 - present AppSeed.us """ from flask import Blueprint blueprint = Blueprint( 'home_blueprint', __name__, url_prefix='' ) ================================================ FILE: apps/home/routes.py ================================================ # -*- encoding: utf-8 -*- """ Copyright (c) 2019 - present AppSeed.us """ from apps.home import blueprint from flask import render_template, request from flask_login import login_required, current_user from jinja2 import TemplateNotFound @blueprint.route('/index') @login_required def index(): return render_template('home/index.html', segment='index', user_id=current_user.id) @blueprint.route('/