Compare commits
5 Commits
v1.0.2-rc1
...
v1.0.4
| Author | SHA1 | Date | |
|---|---|---|---|
| bc4d5dc6d9 | |||
| 295e1a78e6 | |||
| 32f93f463e | |||
| 94e62b6ea9 | |||
| 19f133a1ef |
5
.gitignore
vendored
@ -26,3 +26,8 @@ _templates
|
|||||||
# javascript
|
# javascript
|
||||||
package-lock.json
|
package-lock.json
|
||||||
.vscode/symbols.json
|
.vscode/symbols.json
|
||||||
|
|
||||||
|
apps/static/assets/node_modules
|
||||||
|
apps/static/assets/yarn.lock
|
||||||
|
apps/static/assets/.temp
|
||||||
|
|
||||||
|
|||||||
21
CHANGELOG.md
@ -1,5 +1,26 @@
|
|||||||
# Change Log
|
# Change Log
|
||||||
|
|
||||||
|
## [1.0.4] 2021-11-10
|
||||||
|
### Improvements
|
||||||
|
|
||||||
|
- Bump Codebase: [Flask Dashboard](https://github.com/app-generator/boilerplate-code-flask-dashboard) v2.0.0
|
||||||
|
- Dependencies update (all packages)
|
||||||
|
- Flask==2.0.1 (latest stable version)
|
||||||
|
- Better Code formatting
|
||||||
|
- Improved Files organization
|
||||||
|
- Optimize imports
|
||||||
|
- Docker Scripts Update
|
||||||
|
- Gulp Tooling (SASS Compilation)
|
||||||
|
- Fixes:
|
||||||
|
- Import error caused by WTForms
|
||||||
|
|
||||||
|
## [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-05-11
|
## [1.0.2] 2021-05-11
|
||||||
### Improvements
|
### Improvements
|
||||||
|
|
||||||
|
|||||||
15
Dockerfile
@ -1,11 +1,14 @@
|
|||||||
FROM python:3.6
|
FROM python:3.9
|
||||||
|
|
||||||
ENV FLASK_APP run.py
|
COPY . .
|
||||||
|
|
||||||
COPY run.py gunicorn-cfg.py requirements.txt config.py .env ./
|
# set environment variables
|
||||||
COPY app app
|
ENV PYTHONDONTWRITEBYTECODE 1
|
||||||
|
ENV PYTHONUNBUFFERED 1
|
||||||
|
|
||||||
RUN pip install -r requirements.txt
|
# install python dependencies
|
||||||
|
RUN pip install --upgrade pip
|
||||||
|
RUN pip install --no-cache-dir -r requirements.txt
|
||||||
|
|
||||||
EXPOSE 5005
|
# gunicorn
|
||||||
CMD ["gunicorn", "--config", "gunicorn-cfg.py", "run:app"]
|
CMD ["gunicorn", "--config", "gunicorn-cfg.py", "run:app"]
|
||||||
|
|||||||
152
README.md
@ -8,6 +8,8 @@
|
|||||||
|
|
||||||
> Free product - **Flask Dashboard** starter project - Features:
|
> Free product - **Flask Dashboard** starter project - Features:
|
||||||
|
|
||||||
|
- Up-to-date [dependencies](./requirements.txt): **Flask 2.0.1**
|
||||||
|
- [SCSS compilation](#recompile-css) via **Gulp**
|
||||||
- UI Kit: **Black Dashboard** (Free Version) provided by **[Creative-Tim](https://www.creative-tim.com/)**
|
- UI Kit: **Black Dashboard** (Free Version) provided by **[Creative-Tim](https://www.creative-tim.com/)**
|
||||||
- Flask Codebase - provided by **[AppSeed](https://appseed.us/)**
|
- Flask Codebase - provided by **[AppSeed](https://appseed.us/)**
|
||||||
- SQLite, PostgreSQL, SQLAlchemy ORM
|
- SQLite, PostgreSQL, SQLAlchemy ORM
|
||||||
@ -22,6 +24,7 @@
|
|||||||
## Table of Contents
|
## Table of Contents
|
||||||
|
|
||||||
* [Demo](#demo)
|
* [Demo](#demo)
|
||||||
|
* [Docker Support](#docker-support)
|
||||||
* [Quick Start](#quick-start)
|
* [Quick Start](#quick-start)
|
||||||
* [Documentation](#documentation)
|
* [Documentation](#documentation)
|
||||||
* [File Structure](#file-structure)
|
* [File Structure](#file-structure)
|
||||||
@ -42,6 +45,27 @@
|
|||||||
|
|
||||||
<br />
|
<br />
|
||||||
|
|
||||||
|
## Docker Support
|
||||||
|
|
||||||
|
> Get the code
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ git clone https://github.com/app-generator/black-dashboard-flask.git
|
||||||
|
$ cd black-dashboard-flask
|
||||||
|
```
|
||||||
|
|
||||||
|
> Start the app in Docker
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ docker-compose pull # download dependencies
|
||||||
|
$ docker-compose build # local set up
|
||||||
|
$ docker-compose up -d # start the app
|
||||||
|
```
|
||||||
|
|
||||||
|
Visit `http://localhost:85` in your browser. The app should be up & running.
|
||||||
|
|
||||||
|
<br />
|
||||||
|
|
||||||
## Quick start
|
## Quick start
|
||||||
|
|
||||||
> UNZIP the sources or clone the private repository. After getting the code, open a terminal and navigate to the working directory, with product source code.
|
> UNZIP the sources or clone the private repository. After getting the code, open a terminal and navigate to the working directory, with product source code.
|
||||||
@ -98,41 +122,115 @@ Within the download you'll find the following directories and files:
|
|||||||
```bash
|
```bash
|
||||||
< PROJECT ROOT >
|
< PROJECT ROOT >
|
||||||
|
|
|
|
||||||
|-- app/
|
|-- apps/
|
||||||
| |-- home/ # Home Blueprint - serve app pages (private area)
|
| |
|
||||||
| |-- base/ # Base Blueprint - handles the authentication
|
| |-- home/ # A simple app that serve HTML files
|
||||||
| |-- static/
|
| | |-- routes.py # Define app routes
|
||||||
| | |-- <css, JS, images> # CSS files, Javascripts files
|
| |
|
||||||
| |
|
| |-- authentication/ # Handles auth routes (login and register)
|
||||||
| |-- templates/ # Templates used to render pages
|
| | |-- routes.py # Define authentication routes
|
||||||
| |
|
| | |-- models.py # Defines models
|
||||||
| |-- includes/ #
|
| | |-- forms.py # Define auth forms (login and register)
|
||||||
| | |-- navigation.html # Top menu component
|
| |
|
||||||
| | |-- sidebar.html # Sidebar component
|
| |-- static/
|
||||||
| | |-- footer.html # App Footer
|
| | |-- <css, JS, images> # CSS files, Javascripts files
|
||||||
| | |-- scripts.html # Scripts common to all pages
|
| |
|
||||||
| |
|
| |-- templates/ # Templates used to render pages
|
||||||
| |-- layouts/ # Master pages
|
| | |-- includes/ # HTML chunks and components
|
||||||
| | |-- base-fullscreen.html # Used by Authentication pages
|
| | | |-- navigation.html # Top menu component
|
||||||
| | |-- base.html # Used by common pages
|
| | | |-- sidebar.html # Sidebar component
|
||||||
| |
|
| | | |-- footer.html # App Footer
|
||||||
| |-- accounts/ # Authentication pages
|
| | | |-- scripts.html # Scripts common to all pages
|
||||||
| |-- login.html # Login page
|
| | |
|
||||||
| |-- register.html # Registration page
|
| | |-- layouts/ # Master pages
|
||||||
|
| | | |-- base-fullscreen.html # Used by Authentication pages
|
||||||
|
| | | |-- base.html # Used by common pages
|
||||||
|
| | |
|
||||||
|
| | |-- accounts/ # Authentication pages
|
||||||
|
| | | |-- login.html # Login page
|
||||||
|
| | | |-- register.html # Register page
|
||||||
|
| | |
|
||||||
|
| | |-- home/ # UI Kit Pages
|
||||||
|
| | |-- index.html # Index page
|
||||||
|
| | |-- 404-page.html # 404 page
|
||||||
|
| | |-- *.html # All other pages
|
||||||
|
| |
|
||||||
|
| config.py # Set up the app
|
||||||
|
| __init__.py # Initialize the app
|
||||||
|
|
|
|
||||||
|-- requirements.txt # Development modules - SQLite storage
|
|-- requirements.txt # Development modules - SQLite storage
|
||||||
|-- requirements-mysql.txt # Production modules - Mysql DMBS
|
|-- requirements-mysql.txt # Production modules - Mysql DMBS
|
||||||
|-- requirements-pqsql.txt # Production modules - PostgreSql DMBS
|
|-- requirements-pqsql.txt # Production modules - PostgreSql DMBS
|
||||||
|
|
|
|
||||||
|-- .env # Inject Configuration via Environment
|
|-- Dockerfile # Deployment
|
||||||
|-- config.py # Set up the app
|
|-- docker-compose.yml # Deployment
|
||||||
|-- run.py # Start the app - WSGI gateway
|
|-- gunicorn-cfg.py # Deployment
|
||||||
|
|-- nginx # Deployment
|
||||||
|
| |-- appseed-app.conf # Deployment
|
||||||
|
|
|
||||||
|
|-- .env # Inject Configuration via Environment
|
||||||
|
|-- run.py # Start the app - WSGI gateway
|
||||||
|
|
|
|
||||||
|-- ************************************************************************
|
|-- ************************************************************************
|
||||||
```
|
```
|
||||||
|
|
||||||
<br />
|
<br />
|
||||||
|
|
||||||
|
> The bootstrap flow
|
||||||
|
|
||||||
|
- `run.py` loads the `.env` file
|
||||||
|
- Initialize the app using the specified profile: *Debug* or *Production*
|
||||||
|
- If env.DEBUG is set to *True* the SQLite storage is used
|
||||||
|
- If env.DEBUG is set to *False* the specified DB driver is used (MySql, PostgreSQL)
|
||||||
|
- Call the app factory method `create_app` defined in app/__init__.py
|
||||||
|
- Redirect the guest users to Login page
|
||||||
|
- Unlock the pages served by *home* blueprint for authenticated users
|
||||||
|
|
||||||
|
<br />
|
||||||
|
|
||||||
|
## Recompile CSS
|
||||||
|
|
||||||
|
To recompile SCSS files, follow this setup:
|
||||||
|
|
||||||
|
<br />
|
||||||
|
|
||||||
|
**Step #1** - Install tools
|
||||||
|
|
||||||
|
- [NodeJS](https://nodejs.org/en/) 12.x or higher
|
||||||
|
- [Gulp](https://gulpjs.com/) - globally
|
||||||
|
- `npm install -g gulp-cli`
|
||||||
|
- [Yarn](https://yarnpkg.com/) (optional)
|
||||||
|
|
||||||
|
<br />
|
||||||
|
|
||||||
|
**Step #2** - Change the working directory to `assets` folder
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ cd apps/static/assets
|
||||||
|
```
|
||||||
|
|
||||||
|
<br />
|
||||||
|
|
||||||
|
**Step #3** - Install modules (this will create a classic `node_modules` directory)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ npm install
|
||||||
|
// OR
|
||||||
|
$ yarn
|
||||||
|
```
|
||||||
|
|
||||||
|
<br />
|
||||||
|
|
||||||
|
**Step #4** - Edit & Recompile SCSS files
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ gulp scss
|
||||||
|
```
|
||||||
|
|
||||||
|
The generated file is saved in `static/assets/css` directory.
|
||||||
|
|
||||||
|
<br />
|
||||||
|
|
||||||
## Browser Support
|
## Browser Support
|
||||||
|
|
||||||
At present, we officially aim to support the last two versions of the following browsers:
|
At present, we officially aim to support the last two versions of the following browsers:
|
||||||
|
|||||||
@ -1,19 +0,0 @@
|
|||||||
# -*- encoding: utf-8 -*-
|
|
||||||
"""
|
|
||||||
Copyright (c) 2019 - present AppSeed.us
|
|
||||||
"""
|
|
||||||
|
|
||||||
from flask_wtf import FlaskForm
|
|
||||||
from wtforms import TextField, PasswordField
|
|
||||||
from wtforms.validators import InputRequired, Email, DataRequired
|
|
||||||
|
|
||||||
## login and registration
|
|
||||||
|
|
||||||
class LoginForm(FlaskForm):
|
|
||||||
username = TextField ('Username', id='username_login' , validators=[DataRequired()])
|
|
||||||
password = PasswordField('Password', id='pwd_login' , validators=[DataRequired()])
|
|
||||||
|
|
||||||
class CreateAccountForm(FlaskForm):
|
|
||||||
username = TextField('Username' , id='username_create' , validators=[DataRequired()])
|
|
||||||
email = TextField('Email' , id='email_create' , validators=[DataRequired(), Email()])
|
|
||||||
password = PasswordField('Password' , id='pwd_create' , validators=[DataRequired()])
|
|
||||||
@ -1,112 +0,0 @@
|
|||||||
# -*- encoding: utf-8 -*-
|
|
||||||
"""
|
|
||||||
Copyright (c) 2019 - present AppSeed.us
|
|
||||||
"""
|
|
||||||
|
|
||||||
from flask import jsonify, render_template, redirect, request, url_for
|
|
||||||
from flask_login import (
|
|
||||||
current_user,
|
|
||||||
login_required,
|
|
||||||
login_user,
|
|
||||||
logout_user
|
|
||||||
)
|
|
||||||
|
|
||||||
from app import db, login_manager
|
|
||||||
from app.base import blueprint
|
|
||||||
from app.base.forms import LoginForm, CreateAccountForm
|
|
||||||
from app.base.models import User
|
|
||||||
|
|
||||||
from app.base.util import verify_pass
|
|
||||||
|
|
||||||
@blueprint.route('/')
|
|
||||||
def route_default():
|
|
||||||
return redirect(url_for('base_blueprint.login'))
|
|
||||||
|
|
||||||
## Login & Registration
|
|
||||||
|
|
||||||
@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 = User.query.filter_by(username=username).first()
|
|
||||||
|
|
||||||
# Check the password
|
|
||||||
if user and verify_pass( password, user.password):
|
|
||||||
|
|
||||||
login_user(user)
|
|
||||||
return redirect(url_for('base_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():
|
|
||||||
login_form = LoginForm(request.form)
|
|
||||||
create_account_form = CreateAccountForm(request.form)
|
|
||||||
if 'register' in request.form:
|
|
||||||
|
|
||||||
username = request.form['username']
|
|
||||||
email = request.form['email' ]
|
|
||||||
|
|
||||||
# Check usename exists
|
|
||||||
user = User.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 = User.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 = User(**request.form)
|
|
||||||
db.session.add(user)
|
|
||||||
db.session.commit()
|
|
||||||
|
|
||||||
return render_template( 'accounts/register.html',
|
|
||||||
msg='User created please <a href="/login">login</a>',
|
|
||||||
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('base_blueprint.login'))
|
|
||||||
|
|
||||||
## Errors
|
|
||||||
|
|
||||||
@login_manager.unauthorized_handler
|
|
||||||
def unauthorized_handler():
|
|
||||||
return render_template('page-403.html'), 403
|
|
||||||
|
|
||||||
@blueprint.errorhandler(403)
|
|
||||||
def access_forbidden(error):
|
|
||||||
return render_template('page-403.html'), 403
|
|
||||||
|
|
||||||
@blueprint.errorhandler(404)
|
|
||||||
def not_found_error(error):
|
|
||||||
return render_template('page-404.html'), 404
|
|
||||||
|
|
||||||
@blueprint.errorhandler(500)
|
|
||||||
def internal_error(error):
|
|
||||||
return render_template('page-500.html'), 500
|
|
||||||
@ -1 +0,0 @@
|
|||||||
{"version":3,"sources":["_site_dashboard_free/assets/js/dashboard-free.js"],"names":["transparent","transparentDemo","fixedTop","navbar_initialized","backgroundOrange","sidebar_mini_active","toggle_initialized","$html","$","$body","$navbar_minimize_fixed","$collapse","$navbar","$tagsinput","$selectpicker","$navbar_color","$full_screen_map","$datetimepicker","$datepicker","$timepicker","seq","delays","durations","seq2","delays2","durations2","hexToRGB","hex","alpha","r","parseInt","slice","g","b","navigator","platform","indexOf","length","PerfectScrollbar","wheelSpeed","wheelPropagation","minScrollbarLength","suppressScrollX","each","this","addClass","document","ready","offset","window","scroll","scrollTop","css","top","on","closest","removeClass","blackDashboard","initMinimizeSidebar","scroll_distance","attr","checkScrollForTransparentNavbar","parent","$this","data_on_label","data","data_off_label","bootstrapSwitch","onText","offText","$toggle","misc","navbar_menu_visible","setTimeout","remove","appendTo","click","resize","isExpanded","find","hasClass","width","showSidebarMessage","simulateWindowResize","setInterval","dispatchEvent","Event","clearInterval","message","notify","icon","type","timer","placement","from","align","e","console","log"],"mappings":"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"}
|
|
||||||
@ -1,299 +0,0 @@
|
|||||||
.white-content{
|
|
||||||
|
|
||||||
background: $light-bg;
|
|
||||||
|
|
||||||
.navbar.navbar-transparent .navbar-brand{
|
|
||||||
color: $black-states;
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar.navbar-transparent .navbar-toggler-bar{
|
|
||||||
background: $black-states;
|
|
||||||
}
|
|
||||||
.navbar.navbar-transparent .navbar-nav li a:not(.dropdown-item){
|
|
||||||
color: $black-states;
|
|
||||||
& i{
|
|
||||||
color: $black-states;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar.navbar-transparent .navbar-minimize button i{
|
|
||||||
color: $black-states;
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar.navbar-transparent .search-bar.input-group i{
|
|
||||||
color: $black-states;
|
|
||||||
}
|
|
||||||
.navbar.navbar-transparent .search-bar.input-group .form-control{
|
|
||||||
color: $default;
|
|
||||||
&::placeholder{
|
|
||||||
color: $dark-gray;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar{
|
|
||||||
box-shadow: 0 2px 22px 0 rgba(0,0,0,.1), 0 4px 20px 0 rgba(0,0,0,.15);
|
|
||||||
p{
|
|
||||||
color: $opacity-8;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.main-panel{
|
|
||||||
background: $light-bg;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1, h2, h3, h4, h5, h6, p, ol li, ul li, pre {
|
|
||||||
color: $black-states;
|
|
||||||
}
|
|
||||||
|
|
||||||
.font-icon-detail i{
|
|
||||||
color: $black;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn:not([data-action]):hover{
|
|
||||||
box-shadow: 0 4px 6px rgba(50,50,93,.11), 0 1px 3px rgba(0,0,0,.08);
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-neutral.btn-link{
|
|
||||||
color: rgba($black, 0.7);
|
|
||||||
}
|
|
||||||
|
|
||||||
//style for inputs
|
|
||||||
|
|
||||||
@include form-control-placeholder(rgba($black,0.4), 1);
|
|
||||||
.has-danger{
|
|
||||||
.form-control, .input-group-prepend .input-group-text{
|
|
||||||
border-color: $danger-states;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.input-group-prepend .input-group-text{
|
|
||||||
border-color: rgba($black-states,0.5);
|
|
||||||
color: $black-states;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-control{
|
|
||||||
color: $black;
|
|
||||||
border-color: rgba($black-states,0.5);
|
|
||||||
&:focus{
|
|
||||||
border-color: $primary;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-group.no-border,
|
|
||||||
.input-group.no-border{
|
|
||||||
.form-control,
|
|
||||||
.form-control + .input-group-prepend .input-group-text,
|
|
||||||
.form-control + .input-group-append .input-group-text,
|
|
||||||
.input-group-prepend .input-group-text,
|
|
||||||
.input-group-append .input-group-text{
|
|
||||||
background-color: $opacity-gray-3;
|
|
||||||
&:focus,
|
|
||||||
&:active,
|
|
||||||
&:active{
|
|
||||||
background-color: $opacity-gray-5;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-control{
|
|
||||||
&:focus{
|
|
||||||
& + .input-group-prepend .input-group-text,
|
|
||||||
& + .input-group-append .input-group-text{
|
|
||||||
|
|
||||||
background-color: $transparent-bg;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
.input-group[disabled]{
|
|
||||||
.input-group-prepend .input-group-text,
|
|
||||||
.input-group-append .input-group-text{
|
|
||||||
background-color: $black;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-control[disabled], .form-control[readonly], fieldset[disabled] .form-control{
|
|
||||||
background: $light-gray;
|
|
||||||
border-color: rgba($black-states,0.3);
|
|
||||||
}
|
|
||||||
|
|
||||||
.input-group-focus{
|
|
||||||
.input-group-prepend .input-group-text,
|
|
||||||
.input-group-append .input-group-text,
|
|
||||||
.form-control{
|
|
||||||
border-color: $primary;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.no-border{
|
|
||||||
.input-group-prepend .input-group-text,
|
|
||||||
.input-group-append .input-group-text{
|
|
||||||
|
|
||||||
background-color: $opacity-gray-5;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.input-group-prepend .input-group-text {
|
|
||||||
border-right: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.input-group-append .input-group-text {
|
|
||||||
border-left: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.has-danger .form-control:focus, .has-success.input-group-focus .input-group-append .input-group-text, .has-success.input-group-focus .input-group-prepend .input-group-text {
|
|
||||||
border-color: $danger-states;
|
|
||||||
}
|
|
||||||
|
|
||||||
.has-success .form-control:focus, .has-success.input-group-focus .input-group-append .input-group-text, .has-success.input-group-focus .input-group-prepend .input-group-text {
|
|
||||||
border-color: darken($success, 10%);
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn.btn-link{
|
|
||||||
&:hover,&:active, &:focus{
|
|
||||||
color: $dark-gray !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-group .btn.active{
|
|
||||||
box-shadow: 0 4px 6px rgba(50,50,93,.11), 0 1px 3px rgba(0,0,0,.08);
|
|
||||||
}
|
|
||||||
|
|
||||||
.card:not(.card-white){
|
|
||||||
background: $white;
|
|
||||||
box-shadow: 0 1px 15px 0 rgba(, , , 0.05);
|
|
||||||
.card-header{
|
|
||||||
color: $black;
|
|
||||||
a[data-toggle="collapse"]{
|
|
||||||
color: $black;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.card-header .card-title, .card-body .card-title{
|
|
||||||
color: $black-states;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-body{
|
|
||||||
.card-category, .card-description{
|
|
||||||
color: $black-states;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
label:not(.btn){
|
|
||||||
color: $default;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
&.nav-pills .nav-item .nav-link {
|
|
||||||
color: $nav-gray;
|
|
||||||
background-color: hsla(0,0%,87%,.3);
|
|
||||||
&:not(.active):hover{
|
|
||||||
background: hsla(0,0%,87%,.5);
|
|
||||||
}
|
|
||||||
&.active{
|
|
||||||
color: $white;
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.tab-content .tab-pane {
|
|
||||||
color: $nav-gray;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
.card{
|
|
||||||
box-shadow: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.card-plain{
|
|
||||||
background: $transparent-bg;
|
|
||||||
box-shadow: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.card-tasks{
|
|
||||||
.card-body{
|
|
||||||
i{
|
|
||||||
color: rgba(34,42,66,0.7);
|
|
||||||
&:hover{
|
|
||||||
color: $black;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.table{
|
|
||||||
|
|
||||||
> tbody > tr > td {
|
|
||||||
color: rgba($black, 0.7) !important;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
> thead > tr > th,
|
|
||||||
> tbody > tr > th,
|
|
||||||
> tfoot > tr > th,
|
|
||||||
> thead > tr > td,
|
|
||||||
> tbody > tr > td,
|
|
||||||
> tfoot > tr > td{
|
|
||||||
border-color: rgba($black, 0.2);
|
|
||||||
padding: 12px 7px;
|
|
||||||
vertical-align: middle;
|
|
||||||
}
|
|
||||||
|
|
||||||
> thead > tr > th, button.btn-neutral.btn-link{
|
|
||||||
color: rgba($black, 0.7);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.footer ul li a{
|
|
||||||
color: $black-states;
|
|
||||||
}
|
|
||||||
|
|
||||||
.footer .copyright{
|
|
||||||
color: $black-states;
|
|
||||||
}
|
|
||||||
|
|
||||||
.progress-container, .progress-container.progress.sm{
|
|
||||||
.progress{
|
|
||||||
background: rgba($black,0.1);
|
|
||||||
box-shadow: 0 0 0 3px rgba($black,0.1);
|
|
||||||
.progress-value{
|
|
||||||
color: $default;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.progress-badge{
|
|
||||||
color: $black-states;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.full-page{
|
|
||||||
background: $light-bg;
|
|
||||||
|
|
||||||
h1, h2, h3, h4, h5, h6, p, ol li, ul li, pre {
|
|
||||||
color: $black-states;
|
|
||||||
}
|
|
||||||
|
|
||||||
.description{
|
|
||||||
color: $dark-gray;
|
|
||||||
}
|
|
||||||
|
|
||||||
.footer ul li a{
|
|
||||||
color: $black-states;
|
|
||||||
}
|
|
||||||
|
|
||||||
.footer .copyright{
|
|
||||||
color: $black-states;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-pills .nav-item .nav-link:not(.active){
|
|
||||||
background: darken($light-bg,10%);
|
|
||||||
color: $black-states;
|
|
||||||
&:hover{
|
|
||||||
background: darken($white,20%);
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,70 +0,0 @@
|
|||||||
{% extends "layouts/base.html" %}
|
|
||||||
|
|
||||||
{% block title %} Login {% endblock %}
|
|
||||||
|
|
||||||
<!-- Specific Page CSS goes HERE -->
|
|
||||||
{% block stylesheets %}{% endblock stylesheets %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-8">
|
|
||||||
<div class="card">
|
|
||||||
|
|
||||||
<form role="form" method="post" action="">
|
|
||||||
|
|
||||||
{{ form.hidden_tag() }}
|
|
||||||
|
|
||||||
<div class="card-header">
|
|
||||||
<h5 class="title">Login</h5>
|
|
||||||
|
|
||||||
<h6 class="card-category">
|
|
||||||
{% if msg %}
|
|
||||||
{{ msg | safe }}
|
|
||||||
{% else %}
|
|
||||||
Add your credentials
|
|
||||||
{% endif %}
|
|
||||||
</h6>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="card-body">
|
|
||||||
|
|
||||||
<div class="row">
|
|
||||||
|
|
||||||
<div class="col-md-3 px-md-1">
|
|
||||||
<div class="form-group">
|
|
||||||
<label>Username</label>
|
|
||||||
{{ form.username(placeholder="Username", class="form-control") }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<div class="row">
|
|
||||||
|
|
||||||
<div class="col-md-3 px-md-1">
|
|
||||||
<div class="form-group">
|
|
||||||
<label>Password</label>
|
|
||||||
{{ form.password(placeholder="Password", class="form-control", type="password") }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<div class="card-footer">
|
|
||||||
<button type="submit" name="login" class="btn btn-fill btn-primary">Login</button>
|
|
||||||
|
|
||||||
Don't have an account? <a href="{{ url_for('base_blueprint.register') }}" class="text-primary">Create</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</form>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
{% endblock content %}
|
|
||||||
|
|
||||||
<!-- Specific Page JS goes HERE -->
|
|
||||||
{% block javascripts %}{% endblock javascripts %}
|
|
||||||
@ -1,74 +0,0 @@
|
|||||||
{% extends "layouts/base.html" %}
|
|
||||||
|
|
||||||
{% block title %} Register {% endblock %}
|
|
||||||
|
|
||||||
<!-- Specific Page CSS goes HERE -->
|
|
||||||
{% block stylesheets %}{% endblock stylesheets %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-8">
|
|
||||||
<div class="card">
|
|
||||||
|
|
||||||
<form role="form" method="post" action="">
|
|
||||||
|
|
||||||
<div class="card-header">
|
|
||||||
<h5 class="title">Register</h5>
|
|
||||||
|
|
||||||
<h6 class="card-category">
|
|
||||||
{% if msg %}
|
|
||||||
{{ msg | safe }}
|
|
||||||
{% else %}
|
|
||||||
Add your credentials
|
|
||||||
{% endif %}
|
|
||||||
</h6>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="card-body">
|
|
||||||
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-3 px-md-1">
|
|
||||||
<div class="form-group">
|
|
||||||
<label>Username</label>
|
|
||||||
{{ form.username(placeholder="Username", class="form-control") }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-3 px-md-1">
|
|
||||||
<div class="form-group">
|
|
||||||
<label>Email</label>
|
|
||||||
{{ form.email(placeholder="Email", class="input form-control", type="email") }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-3 px-md-1">
|
|
||||||
<div class="form-group">
|
|
||||||
<label>Password</label>
|
|
||||||
{{ form.password(placeholder="Password", class="form-control", type="password") }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<div class="card-footer">
|
|
||||||
<button type="submit" name="register" class="btn btn-fill btn-primary">Register</button>
|
|
||||||
|
|
||||||
Have an account? <a href="{{ url_for('base_blueprint.login') }}" class="text-primary">Login</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</form>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
{% endblock content %}
|
|
||||||
|
|
||||||
<!-- Specific Page JS goes HERE -->
|
|
||||||
{% block javascripts %}{% endblock javascripts %}
|
|
||||||
@ -1,82 +0,0 @@
|
|||||||
|
|
||||||
<!-- Navbar -->
|
|
||||||
<nav class="navbar navbar-expand-lg navbar-absolute navbar-transparent">
|
|
||||||
<div class="container-fluid">
|
|
||||||
<div class="navbar-wrapper">
|
|
||||||
<div class="navbar-toggle d-inline">
|
|
||||||
<button type="button" class="navbar-toggler">
|
|
||||||
<span class="navbar-toggler-bar bar1"></span>
|
|
||||||
<span class="navbar-toggler-bar bar2"></span>
|
|
||||||
<span class="navbar-toggler-bar bar3"></span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<a class="navbar-brand" href="javascript:void(0)">Dashboard</a>
|
|
||||||
</div>
|
|
||||||
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navigation" aria-expanded="false" aria-label="Toggle navigation">
|
|
||||||
<span class="navbar-toggler-bar navbar-kebab"></span>
|
|
||||||
<span class="navbar-toggler-bar navbar-kebab"></span>
|
|
||||||
<span class="navbar-toggler-bar navbar-kebab"></span>
|
|
||||||
</button>
|
|
||||||
<div class="collapse navbar-collapse" id="navigation">
|
|
||||||
|
|
||||||
{% if current_user.is_authenticated %}
|
|
||||||
|
|
||||||
<ul class="navbar-nav ml-auto">
|
|
||||||
<li class="search-bar input-group">
|
|
||||||
<button class="btn btn-link" id="search-button" data-toggle="modal" data-target="#searchModal"><i class="tim-icons icon-zoom-split" ></i>
|
|
||||||
<span class="d-lg-none d-md-block">Search</span>
|
|
||||||
</button>
|
|
||||||
</li>
|
|
||||||
<li class="dropdown nav-item">
|
|
||||||
<a href="javascript:void(0)" class="dropdown-toggle nav-link" data-toggle="dropdown">
|
|
||||||
<div class="notification d-none d-lg-block d-xl-block"></div>
|
|
||||||
<i class="tim-icons icon-sound-wave"></i>
|
|
||||||
<p class="d-lg-none">
|
|
||||||
Notifications
|
|
||||||
</p>
|
|
||||||
</a>
|
|
||||||
<ul class="dropdown-menu dropdown-menu-right dropdown-navbar">
|
|
||||||
<li class="nav-link"><a href="#" class="nav-item dropdown-item">Mike John responded to your email</a></li>
|
|
||||||
<li class="nav-link"><a href="javascript:void(0)" class="nav-item dropdown-item">You have 5 more tasks</a></li>
|
|
||||||
<li class="nav-link"><a href="javascript:void(0)" class="nav-item dropdown-item">Your friend Michael is in town</a></li>
|
|
||||||
<li class="nav-link"><a href="javascript:void(0)" class="nav-item dropdown-item">Another notification</a></li>
|
|
||||||
<li class="nav-link"><a href="javascript:void(0)" class="nav-item dropdown-item">Another one</a></li>
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
<li class="dropdown nav-item">
|
|
||||||
<a href="#" class="dropdown-toggle nav-link" data-toggle="dropdown">
|
|
||||||
<div class="photo">
|
|
||||||
<img src="/static/assets/img/anime3.png" alt="Profile Photo">
|
|
||||||
</div>
|
|
||||||
<b class="caret d-none d-lg-block d-xl-block"></b>
|
|
||||||
<p class="d-lg-none">
|
|
||||||
Log out
|
|
||||||
</p>
|
|
||||||
</a>
|
|
||||||
<ul class="dropdown-menu dropdown-navbar">
|
|
||||||
<li class="nav-link"><a href="/page-user.html" class="nav-item dropdown-item">Profile</a></li>
|
|
||||||
<li class="dropdown-divider"></li>
|
|
||||||
<li class="nav-link"><a href="{{ url_for('base_blueprint.logout') }}" class="nav-item dropdown-item">Log out</a></li>
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
<li class="separator d-lg-none"></li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</nav>
|
|
||||||
<div class="modal modal-search fade" id="searchModal" tabindex="-1" role="dialog" aria-labelledby="searchModal" aria-hidden="true">
|
|
||||||
<div class="modal-dialog" role="document">
|
|
||||||
<div class="modal-content">
|
|
||||||
<div class="modal-header">
|
|
||||||
<input type="text" class="form-control" id="inlineFormInputGroup" placeholder="SEARCH">
|
|
||||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
|
||||||
<i class="tim-icons icon-simple-remove"></i>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!-- End Navbar -->
|
|
||||||
@ -1,52 +0,0 @@
|
|||||||
# -*- encoding: utf-8 -*-
|
|
||||||
"""
|
|
||||||
Copyright (c) 2019 - present AppSeed.us
|
|
||||||
"""
|
|
||||||
|
|
||||||
from app.home import blueprint
|
|
||||||
from flask import render_template, redirect, url_for, request
|
|
||||||
from flask_login import login_required, current_user
|
|
||||||
from app import login_manager
|
|
||||||
from jinja2 import TemplateNotFound
|
|
||||||
|
|
||||||
@blueprint.route('/index')
|
|
||||||
@login_required
|
|
||||||
def index():
|
|
||||||
|
|
||||||
return render_template('index.html', segment='index')
|
|
||||||
|
|
||||||
@blueprint.route('/<template>')
|
|
||||||
@login_required
|
|
||||||
def route_template(template):
|
|
||||||
|
|
||||||
try:
|
|
||||||
|
|
||||||
if not template.endswith( '.html' ):
|
|
||||||
template += '.html'
|
|
||||||
|
|
||||||
# Detect the current page
|
|
||||||
segment = get_segment( request )
|
|
||||||
|
|
||||||
# Serve the file (if exists) from app/templates/FILE.html
|
|
||||||
return render_template( template, segment=segment )
|
|
||||||
|
|
||||||
except TemplateNotFound:
|
|
||||||
return render_template('page-404.html'), 404
|
|
||||||
|
|
||||||
except:
|
|
||||||
return render_template('page-500.html'), 500
|
|
||||||
|
|
||||||
# Helper - Extract current page name from request
|
|
||||||
def get_segment( request ):
|
|
||||||
|
|
||||||
try:
|
|
||||||
|
|
||||||
segment = request.path.split('/')[-1]
|
|
||||||
|
|
||||||
if segment == '':
|
|
||||||
segment = 'index'
|
|
||||||
|
|
||||||
return segment
|
|
||||||
|
|
||||||
except:
|
|
||||||
return None
|
|
||||||
@ -3,25 +3,27 @@
|
|||||||
Copyright (c) 2019 - present AppSeed.us
|
Copyright (c) 2019 - present AppSeed.us
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from flask import Flask, url_for
|
from flask import Flask
|
||||||
from flask_login import LoginManager
|
from flask_login import LoginManager
|
||||||
from flask_sqlalchemy import SQLAlchemy
|
from flask_sqlalchemy import SQLAlchemy
|
||||||
from importlib import import_module
|
from importlib import import_module
|
||||||
from logging import basicConfig, DEBUG, getLogger, StreamHandler
|
|
||||||
from os import path
|
|
||||||
|
|
||||||
db = SQLAlchemy()
|
db = SQLAlchemy()
|
||||||
login_manager = LoginManager()
|
login_manager = LoginManager()
|
||||||
|
|
||||||
|
|
||||||
def register_extensions(app):
|
def register_extensions(app):
|
||||||
db.init_app(app)
|
db.init_app(app)
|
||||||
login_manager.init_app(app)
|
login_manager.init_app(app)
|
||||||
|
|
||||||
|
|
||||||
def register_blueprints(app):
|
def register_blueprints(app):
|
||||||
for module_name in ('base', 'home'):
|
for module_name in ('authentication', 'home'):
|
||||||
module = import_module('app.{}.routes'.format(module_name))
|
module = import_module('apps.{}.routes'.format(module_name))
|
||||||
app.register_blueprint(module.blueprint)
|
app.register_blueprint(module.blueprint)
|
||||||
|
|
||||||
|
|
||||||
def configure_database(app):
|
def configure_database(app):
|
||||||
|
|
||||||
@app.before_first_request
|
@app.before_first_request
|
||||||
@ -32,8 +34,9 @@ def configure_database(app):
|
|||||||
def shutdown_session(exception=None):
|
def shutdown_session(exception=None):
|
||||||
db.session.remove()
|
db.session.remove()
|
||||||
|
|
||||||
|
|
||||||
def create_app(config):
|
def create_app(config):
|
||||||
app = Flask(__name__, static_folder='base/static')
|
app = Flask(__name__)
|
||||||
app.config.from_object(config)
|
app.config.from_object(config)
|
||||||
register_extensions(app)
|
register_extensions(app)
|
||||||
register_blueprints(app)
|
register_blueprints(app)
|
||||||
@ -6,9 +6,7 @@ Copyright (c) 2019 - present AppSeed.us
|
|||||||
from flask import Blueprint
|
from flask import Blueprint
|
||||||
|
|
||||||
blueprint = Blueprint(
|
blueprint = Blueprint(
|
||||||
'base_blueprint',
|
'authentication_blueprint',
|
||||||
__name__,
|
__name__,
|
||||||
url_prefix='',
|
url_prefix=''
|
||||||
template_folder='templates',
|
|
||||||
static_folder='static'
|
|
||||||
)
|
)
|
||||||
31
apps/authentication/forms.py
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
# -*- encoding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
Copyright (c) 2019 - present AppSeed.us
|
||||||
|
"""
|
||||||
|
|
||||||
|
from flask_wtf import FlaskForm
|
||||||
|
from wtforms import TextField, PasswordField
|
||||||
|
from wtforms.validators import Email, DataRequired
|
||||||
|
|
||||||
|
# login and registration
|
||||||
|
|
||||||
|
|
||||||
|
class LoginForm(FlaskForm):
|
||||||
|
username = TextField('Username',
|
||||||
|
id='username_login',
|
||||||
|
validators=[DataRequired()])
|
||||||
|
password = PasswordField('Password',
|
||||||
|
id='pwd_login',
|
||||||
|
validators=[DataRequired()])
|
||||||
|
|
||||||
|
|
||||||
|
class CreateAccountForm(FlaskForm):
|
||||||
|
username = TextField('Username',
|
||||||
|
id='username_create',
|
||||||
|
validators=[DataRequired()])
|
||||||
|
email = TextField('Email',
|
||||||
|
id='email_create',
|
||||||
|
validators=[DataRequired(), Email()])
|
||||||
|
password = PasswordField('Password',
|
||||||
|
id='pwd_create',
|
||||||
|
validators=[DataRequired()])
|
||||||
@ -4,20 +4,19 @@ Copyright (c) 2019 - present AppSeed.us
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
from flask_login import UserMixin
|
from flask_login import UserMixin
|
||||||
from sqlalchemy import Binary, Column, Integer, String
|
|
||||||
|
|
||||||
from app import db, login_manager
|
from apps import db, login_manager
|
||||||
|
|
||||||
from app.base.util import hash_pass
|
from apps.authentication.util import hash_pass
|
||||||
|
|
||||||
class User(db.Model, UserMixin):
|
class Users(db.Model, UserMixin):
|
||||||
|
|
||||||
__tablename__ = 'User'
|
__tablename__ = 'Users'
|
||||||
|
|
||||||
id = Column(Integer, primary_key=True)
|
id = db.Column(db.Integer, primary_key=True)
|
||||||
username = Column(String, unique=True)
|
username = db.Column(db.String(64), unique=True)
|
||||||
email = Column(String, unique=True)
|
email = db.Column(db.String(64), unique=True)
|
||||||
password = Column(Binary)
|
password = db.Column(db.LargeBinary)
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
for property, value in kwargs.items():
|
for property, value in kwargs.items():
|
||||||
@ -29,8 +28,8 @@ class User(db.Model, UserMixin):
|
|||||||
value = value[0]
|
value = value[0]
|
||||||
|
|
||||||
if property == 'password':
|
if property == 'password':
|
||||||
value = hash_pass( value ) # we need bytes here (not plain str)
|
value = hash_pass(value) # we need bytes here (not plain str)
|
||||||
|
|
||||||
setattr(self, property, value)
|
setattr(self, property, value)
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
@ -39,10 +38,11 @@ class User(db.Model, UserMixin):
|
|||||||
|
|
||||||
@login_manager.user_loader
|
@login_manager.user_loader
|
||||||
def user_loader(id):
|
def user_loader(id):
|
||||||
return User.query.filter_by(id=id).first()
|
return Users.query.filter_by(id=id).first()
|
||||||
|
|
||||||
|
|
||||||
@login_manager.request_loader
|
@login_manager.request_loader
|
||||||
def request_loader(request):
|
def request_loader(request):
|
||||||
username = request.form.get('username')
|
username = request.form.get('username')
|
||||||
user = User.query.filter_by(username=username).first()
|
user = Users.query.filter_by(username=username).first()
|
||||||
return user if user else None
|
return user if user else None
|
||||||
127
apps/authentication/routes.py
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
# -*- 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 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.authentication.util import verify_pass
|
||||||
|
|
||||||
|
|
||||||
|
@blueprint.route('/')
|
||||||
|
def route_default():
|
||||||
|
return redirect(url_for('authentication_blueprint.login'))
|
||||||
|
|
||||||
|
|
||||||
|
# Login & Registration
|
||||||
|
|
||||||
|
@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',
|
||||||
|
segment = 'login',
|
||||||
|
msg='Wrong user or password',
|
||||||
|
form=login_form)
|
||||||
|
|
||||||
|
if not current_user.is_authenticated:
|
||||||
|
return render_template('accounts/login.html',
|
||||||
|
segment = 'login',
|
||||||
|
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',
|
||||||
|
segment = 'register',
|
||||||
|
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',
|
||||||
|
segment = 'register',
|
||||||
|
success=False,
|
||||||
|
form=create_account_form)
|
||||||
|
|
||||||
|
# else we can create the user
|
||||||
|
user = Users(**request.form)
|
||||||
|
db.session.add(user)
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
return render_template('accounts/register.html',
|
||||||
|
msg='User created please <a href="/login">login</a>',
|
||||||
|
segment = 'register',
|
||||||
|
success=True,
|
||||||
|
form=create_account_form)
|
||||||
|
|
||||||
|
else:
|
||||||
|
return render_template( 'accounts/register.html',
|
||||||
|
segment = 'register',
|
||||||
|
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
|
||||||
@ -2,28 +2,33 @@
|
|||||||
"""
|
"""
|
||||||
Copyright (c) 2019 - present AppSeed.us
|
Copyright (c) 2019 - present AppSeed.us
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import hashlib, binascii, os
|
import os
|
||||||
|
import hashlib
|
||||||
|
import binascii
|
||||||
|
|
||||||
# Inspiration -> https://www.vitoshacademy.com/hashing-passwords-in-python/
|
# Inspiration -> https://www.vitoshacademy.com/hashing-passwords-in-python/
|
||||||
|
|
||||||
def hash_pass( password ):
|
|
||||||
|
def hash_pass(password):
|
||||||
"""Hash a password for storing."""
|
"""Hash a password for storing."""
|
||||||
|
|
||||||
salt = hashlib.sha256(os.urandom(60)).hexdigest().encode('ascii')
|
salt = hashlib.sha256(os.urandom(60)).hexdigest().encode('ascii')
|
||||||
pwdhash = hashlib.pbkdf2_hmac('sha512', password.encode('utf-8'),
|
pwdhash = hashlib.pbkdf2_hmac('sha512', password.encode('utf-8'),
|
||||||
salt, 100000)
|
salt, 100000)
|
||||||
pwdhash = binascii.hexlify(pwdhash)
|
pwdhash = binascii.hexlify(pwdhash)
|
||||||
return (salt + pwdhash) # return bytes
|
return (salt + pwdhash) # return bytes
|
||||||
|
|
||||||
|
|
||||||
def verify_pass(provided_password, stored_password):
|
def verify_pass(provided_password, stored_password):
|
||||||
"""Verify a stored password against one provided by user"""
|
"""Verify a stored password against one provided by user"""
|
||||||
|
|
||||||
stored_password = stored_password.decode('ascii')
|
stored_password = stored_password.decode('ascii')
|
||||||
salt = stored_password[:64]
|
salt = stored_password[:64]
|
||||||
stored_password = stored_password[64:]
|
stored_password = stored_password[64:]
|
||||||
pwdhash = hashlib.pbkdf2_hmac('sha512',
|
pwdhash = hashlib.pbkdf2_hmac('sha512',
|
||||||
provided_password.encode('utf-8'),
|
provided_password.encode('utf-8'),
|
||||||
salt.encode('ascii'),
|
salt.encode('ascii'),
|
||||||
100000)
|
100000)
|
||||||
pwdhash = binascii.hexlify(pwdhash).decode('ascii')
|
pwdhash = binascii.hexlify(pwdhash).decode('ascii')
|
||||||
return pwdhash == stored_password
|
return pwdhash == stored_password
|
||||||
|
|
||||||
@ -4,11 +4,11 @@ Copyright (c) 2019 - present AppSeed.us
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
from decouple import config
|
from decouple import config
|
||||||
|
|
||||||
class Config(object):
|
class Config(object):
|
||||||
|
|
||||||
basedir = os.path.abspath(os.path.dirname(__file__))
|
basedir = os.path.abspath(os.path.dirname(__file__))
|
||||||
|
|
||||||
# Set up the App SECRET_KEY
|
# Set up the App SECRET_KEY
|
||||||
SECRET_KEY = config('SECRET_KEY', default='S#perS3crEt_007')
|
SECRET_KEY = config('SECRET_KEY', default='S#perS3crEt_007')
|
||||||
@ -17,29 +17,32 @@ class Config(object):
|
|||||||
SQLALCHEMY_DATABASE_URI = 'sqlite:///' + os.path.join(basedir, 'db.sqlite3')
|
SQLALCHEMY_DATABASE_URI = 'sqlite:///' + os.path.join(basedir, 'db.sqlite3')
|
||||||
SQLALCHEMY_TRACK_MODIFICATIONS = False
|
SQLALCHEMY_TRACK_MODIFICATIONS = False
|
||||||
|
|
||||||
|
|
||||||
class ProductionConfig(Config):
|
class ProductionConfig(Config):
|
||||||
DEBUG = False
|
DEBUG = False
|
||||||
|
|
||||||
# Security
|
# Security
|
||||||
SESSION_COOKIE_HTTPONLY = True
|
SESSION_COOKIE_HTTPONLY = True
|
||||||
REMEMBER_COOKIE_HTTPONLY = True
|
REMEMBER_COOKIE_HTTPONLY = True
|
||||||
REMEMBER_COOKIE_DURATION = 3600
|
REMEMBER_COOKIE_DURATION = 3600
|
||||||
|
|
||||||
# PostgreSQL database
|
# PostgreSQL database
|
||||||
SQLALCHEMY_DATABASE_URI = '{}://{}:{}@{}:{}/{}'.format(
|
SQLALCHEMY_DATABASE_URI = '{}://{}:{}@{}:{}/{}'.format(
|
||||||
config( 'DB_ENGINE' , default='postgresql' ),
|
config('DB_ENGINE', default='postgresql'),
|
||||||
config( 'DB_USERNAME' , default='appseed' ),
|
config('DB_USERNAME', default='appseed'),
|
||||||
config( 'DB_PASS' , default='pass' ),
|
config('DB_PASS', default='pass'),
|
||||||
config( 'DB_HOST' , default='localhost' ),
|
config('DB_HOST', default='localhost'),
|
||||||
config( 'DB_PORT' , default=5432 ),
|
config('DB_PORT', default=5432),
|
||||||
config( 'DB_NAME' , default='appseed-flask' )
|
config('DB_NAME', default='appseed-flask')
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class DebugConfig(Config):
|
class DebugConfig(Config):
|
||||||
DEBUG = True
|
DEBUG = True
|
||||||
|
|
||||||
|
|
||||||
# Load all possible configurations
|
# Load all possible configurations
|
||||||
config_dict = {
|
config_dict = {
|
||||||
'Production': ProductionConfig,
|
'Production': ProductionConfig,
|
||||||
'Debug' : DebugConfig
|
'Debug': DebugConfig
|
||||||
}
|
}
|
||||||
@ -8,7 +8,5 @@ from flask import Blueprint
|
|||||||
blueprint = Blueprint(
|
blueprint = Blueprint(
|
||||||
'home_blueprint',
|
'home_blueprint',
|
||||||
__name__,
|
__name__,
|
||||||
url_prefix='',
|
url_prefix=''
|
||||||
template_folder='templates',
|
|
||||||
static_folder='static'
|
|
||||||
)
|
)
|
||||||
53
apps/home/routes.py
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
# -*- 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
|
||||||
|
from jinja2 import TemplateNotFound
|
||||||
|
|
||||||
|
|
||||||
|
@blueprint.route('/index')
|
||||||
|
@login_required
|
||||||
|
def index():
|
||||||
|
|
||||||
|
return render_template('home/index.html', segment='index')
|
||||||
|
|
||||||
|
@blueprint.route('/<template>')
|
||||||
|
@login_required
|
||||||
|
def route_template(template):
|
||||||
|
|
||||||
|
try:
|
||||||
|
|
||||||
|
if not template.endswith('.html'):
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Detect the current page
|
||||||
|
segment = get_segment(request)
|
||||||
|
|
||||||
|
# Serve the file (if exists) from app/templates/home/FILE.html
|
||||||
|
return render_template("home/" + template, segment=segment)
|
||||||
|
|
||||||
|
except TemplateNotFound:
|
||||||
|
return render_template('home/page-404.html'), 404
|
||||||
|
|
||||||
|
except:
|
||||||
|
return render_template('home/page-500.html'), 500
|
||||||
|
|
||||||
|
|
||||||
|
# Helper - Extract current page name from request
|
||||||
|
def get_segment(request):
|
||||||
|
|
||||||
|
try:
|
||||||
|
|
||||||
|
segment = request.path.split('/')[-1]
|
||||||
|
|
||||||
|
if segment == '':
|
||||||
|
segment = 'index'
|
||||||
|
|
||||||
|
return segment
|
||||||
|
|
||||||
|
except:
|
||||||
|
return None
|
||||||
13360
apps/static/assets/css/black-dashboard.css
Normal file
1
apps/static/assets/css/black-dashboard.css.map
Normal file
22
apps/static/assets/css/black-dashboard.min.css
vendored
Normal file
59
apps/static/assets/gulpfile.js
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
=========================================================
|
||||||
|
* AppSeed - Simple SCSS compiler via Gulp
|
||||||
|
=========================================================
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
var autoprefixer = require('gulp-autoprefixer');
|
||||||
|
var browserSync = require('browser-sync').create();
|
||||||
|
var cleanCss = require('gulp-clean-css');
|
||||||
|
var gulp = require('gulp');
|
||||||
|
const npmDist = require('gulp-npm-dist');
|
||||||
|
var sass = require('gulp-sass')(require('node-sass'));
|
||||||
|
var wait = require('gulp-wait');
|
||||||
|
var sourcemaps = require('gulp-sourcemaps');
|
||||||
|
var rename = require("gulp-rename");
|
||||||
|
|
||||||
|
// Define COMMON paths
|
||||||
|
|
||||||
|
const paths = {
|
||||||
|
src: {
|
||||||
|
base: './',
|
||||||
|
css: './css',
|
||||||
|
scss: './scss',
|
||||||
|
node_modules: './node_modules/',
|
||||||
|
vendor: './vendor'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Compile SCSS
|
||||||
|
gulp.task('scss', function() {
|
||||||
|
return gulp.src([paths.src.scss + '/black-dashboard.scss'])
|
||||||
|
.pipe(wait(500))
|
||||||
|
.pipe(sourcemaps.init())
|
||||||
|
.pipe(sass().on('error', sass.logError))
|
||||||
|
.pipe(autoprefixer({
|
||||||
|
overrideBrowserslist: ['> 1%']
|
||||||
|
}))
|
||||||
|
.pipe(sourcemaps.write('.'))
|
||||||
|
.pipe(gulp.dest(paths.src.css))
|
||||||
|
.pipe(browserSync.stream());
|
||||||
|
});
|
||||||
|
|
||||||
|
// Minify CSS
|
||||||
|
gulp.task('minify:css', function() {
|
||||||
|
return gulp.src([
|
||||||
|
paths.src.css + '/black-dashboard.css'
|
||||||
|
])
|
||||||
|
.pipe(cleanCss())
|
||||||
|
.pipe(rename(function(path) {
|
||||||
|
// Updates the object in-place
|
||||||
|
path.extname = ".min.css";
|
||||||
|
}))
|
||||||
|
.pipe(gulp.dest(paths.src.css))
|
||||||
|
});
|
||||||
|
|
||||||
|
// Default Task: Compile SCSS and minify the result
|
||||||
|
gulp.task('default', gulp.series('scss', 'minify:css'));
|
||||||
|
Before Width: | Height: | Size: 1.6 MiB After Width: | Height: | Size: 1.6 MiB |
|
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 37 KiB |
|
Before Width: | Height: | Size: 39 KiB After Width: | Height: | Size: 39 KiB |
|
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 2.4 KiB |
|
Before Width: | Height: | Size: 208 KiB After Width: | Height: | Size: 208 KiB |
|
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 2.8 KiB |
|
Before Width: | Height: | Size: 84 KiB After Width: | Height: | Size: 84 KiB |
|
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 655 KiB After Width: | Height: | Size: 655 KiB |
|
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 38 KiB |
|
Before Width: | Height: | Size: 73 KiB After Width: | Height: | Size: 73 KiB |
|
Before Width: | Height: | Size: 75 KiB After Width: | Height: | Size: 75 KiB |
|
Before Width: | Height: | Size: 8.0 KiB After Width: | Height: | Size: 8.0 KiB |
@ -1,2 +1 @@
|
|||||||
var transparent=!0,transparentDemo=!0,fixedTop=!1,navbar_initialized=!1,backgroundOrange=!1,sidebar_mini_active=!1,toggle_initialized=!1,$html=$("html"),$body=$("body"),$navbar_minimize_fixed=$(".navbar-minimize-fixed"),$collapse=$(".collapse"),$navbar=$(".navbar"),$tagsinput=$(".tagsinput"),$selectpicker=$(".selectpicker"),$navbar_color=$(".navbar[color-on-scroll]"),$full_screen_map=$(".full-screen-map"),$datetimepicker=$(".datetimepicker"),$datepicker=$(".datepicker"),$timepicker=$(".timepicker"),seq=0,delays=80,durations=500,seq2=0,delays2=80,durations2=500;function hexToRGB(a,e){var i=parseInt(a.slice(1,3),16),n=parseInt(a.slice(3,5),16),s=parseInt(a.slice(5,7),16);return e?"rgba("+i+", "+n+", "+s+", "+e+")":"rgb("+i+", "+n+", "+s+")"}!function(){if(-1<navigator.platform.indexOf("Win")){if(0!=$(".main-panel").length)new PerfectScrollbar(".main-panel",{wheelSpeed:2,wheelPropagation:!0,minScrollbarLength:20,suppressScrollX:!0});if(0!=$(".sidebar .sidebar-wrapper").length){new PerfectScrollbar(".sidebar .sidebar-wrapper");$(".table-responsive").each(function(){new PerfectScrollbar($(this)[0])})}$html.addClass("perfect-scrollbar-on")}else $html.addClass("perfect-scrollbar-off")}(),$(document).ready(function(){var a=$(".row").offset();(-1<navigator.platform.indexOf("Win")?$(".ps"):$(window)).scroll(function(){50<$(this).scrollTop()?$(".navbar-minimize-fixed").css("opacity","1"):$(".navbar-minimize-fixed").css("opacity","0")}),$(document).scroll(function(){$(this).scrollTop()>a.top?$(".navbar-minimize-fixed").css("opacity","1"):$(".navbar-minimize-fixed").css("opacity","0")}),0==$(".full-screen-map").length&&0==$(".bd-docs").length&&$(".collapse").on("show.bs.collapse",function(){$(this).closest(".navbar").removeClass("navbar-transparent").addClass("bg-white")}).on("hide.bs.collapse",function(){$(this).closest(".navbar").addClass("navbar-transparent").removeClass("bg-white")}),blackDashboard.initMinimizeSidebar(),$navbar=$(".navbar[color-on-scroll]"),scroll_distance=$navbar.attr("color-on-scroll")||500,0!=$(".navbar[color-on-scroll]").length&&(blackDashboard.checkScrollForTransparentNavbar(),$(window).on("scroll",blackDashboard.checkScrollForTransparentNavbar)),$(".form-control").on("focus",function(){$(this).parent(".input-group").addClass("input-group-focus")}).on("blur",function(){$(this).parent(".input-group").removeClass("input-group-focus")}),$(".bootstrap-switch").each(function(){$this=$(this),data_on_label=$this.data("on-label")||"",data_off_label=$this.data("off-label")||"",$this.bootstrapSwitch({onText:data_on_label,offText:data_off_label})})}),$(document).on("click",".navbar-toggle",function(){var a=$(this);if(1==blackDashboard.misc.navbar_menu_visible)$html.removeClass("nav-open"),blackDashboard.misc.navbar_menu_visible=0,setTimeout(function(){a.removeClass("toggled"),$(".bodyClick").remove()},550);else{setTimeout(function(){a.addClass("toggled")},580);$('<div class="bodyClick"></div>').appendTo("body").click(function(){$html.removeClass("nav-open"),blackDashboard.misc.navbar_menu_visible=0,setTimeout(function(){a.removeClass("toggled"),$(".bodyClick").remove()},550)}),$html.addClass("nav-open"),blackDashboard.misc.navbar_menu_visible=1}}),$(window).resize(function(){if(seq=seq2=0,0==$full_screen_map.length&&0==$(".bd-docs").length){var a=$navbar.find('[data-toggle="collapse"]').attr("aria-expanded");$navbar.hasClass("bg-white")&&991<$(window).width()?$navbar.removeClass("bg-white").addClass("navbar-transparent"):$navbar.hasClass("navbar-transparent")&&$(window).width()<991&&"false"!=a&&$navbar.addClass("bg-white").removeClass("navbar-transparent")}}),blackDashboard={misc:{navbar_menu_visible:0},initMinimizeSidebar:function(){0!=$(".sidebar-mini").length&&(sidebar_mini_active=!0),$("#minimizeSidebar").click(function(){$(this);1==sidebar_mini_active?($("body").removeClass("sidebar-mini"),sidebar_mini_active=!1,blackDashboard.showSidebarMessage("Sidebar mini deactivated...")):($("body").addClass("sidebar-mini"),sidebar_mini_active=!0,blackDashboard.showSidebarMessage("Sidebar mini activated..."));var a=setInterval(function(){window.dispatchEvent(new Event("resize"))},180);setTimeout(function(){clearInterval(a)},1e3)})},showSidebarMessage:function(a){try{$.notify({icon:"tim-icons ui-1_bell-53",message:a},{type:"info",timer:4e3,placement:{from:"top",align:"right"}})}catch(a){console.log("Notify library is missing, please make sure you have the notifications library added.")}}};
|
var transparent=!0,transparentDemo=!0,fixedTop=!1,navbar_initialized=!1,backgroundOrange=!1,sidebar_mini_active=!1,toggle_initialized=!1,$html=$("html"),$body=$("body"),$navbar_minimize_fixed=$(".navbar-minimize-fixed"),$collapse=$(".collapse"),$navbar=$(".navbar"),$tagsinput=$(".tagsinput"),$selectpicker=$(".selectpicker"),$navbar_color=$(".navbar[color-on-scroll]"),$full_screen_map=$(".full-screen-map"),$datetimepicker=$(".datetimepicker"),$datepicker=$(".datepicker"),$timepicker=$(".timepicker"),seq=0,delays=80,durations=500,seq2=0,delays2=80,durations2=500;function hexToRGB(a,e){var i=parseInt(a.slice(1,3),16),n=parseInt(a.slice(3,5),16),s=parseInt(a.slice(5,7),16);return e?"rgba("+i+", "+n+", "+s+", "+e+")":"rgb("+i+", "+n+", "+s+")"}!function(){if(-1<navigator.platform.indexOf("Win")){if(0!=$(".main-panel").length)new PerfectScrollbar(".main-panel",{wheelSpeed:2,wheelPropagation:!0,minScrollbarLength:20,suppressScrollX:!0});if(0!=$(".sidebar .sidebar-wrapper").length){new PerfectScrollbar(".sidebar .sidebar-wrapper");$(".table-responsive").each(function(){new PerfectScrollbar($(this)[0])})}$html.addClass("perfect-scrollbar-on")}else $html.addClass("perfect-scrollbar-off")}(),$(document).ready(function(){var a=$(".row").offset();(-1<navigator.platform.indexOf("Win")?$(".ps"):$(window)).scroll(function(){50<$(this).scrollTop()?$(".navbar-minimize-fixed").css("opacity","1"):$(".navbar-minimize-fixed").css("opacity","0")}),$(document).scroll(function(){$(this).scrollTop()>a.top?$(".navbar-minimize-fixed").css("opacity","1"):$(".navbar-minimize-fixed").css("opacity","0")}),0==$(".full-screen-map").length&&0==$(".bd-docs").length&&$(".collapse").on("show.bs.collapse",function(){$(this).closest(".navbar").removeClass("navbar-transparent").addClass("bg-white")}).on("hide.bs.collapse",function(){$(this).closest(".navbar").addClass("navbar-transparent").removeClass("bg-white")}),blackDashboard.initMinimizeSidebar(),$navbar=$(".navbar[color-on-scroll]"),scroll_distance=$navbar.attr("color-on-scroll")||500,0!=$(".navbar[color-on-scroll]").length&&(blackDashboard.checkScrollForTransparentNavbar(),$(window).on("scroll",blackDashboard.checkScrollForTransparentNavbar)),$(".form-control").on("focus",function(){$(this).parent(".input-group").addClass("input-group-focus")}).on("blur",function(){$(this).parent(".input-group").removeClass("input-group-focus")}),$(".bootstrap-switch").each(function(){$this=$(this),data_on_label=$this.data("on-label")||"",data_off_label=$this.data("off-label")||"",$this.bootstrapSwitch({onText:data_on_label,offText:data_off_label})})}),$(document).on("click",".navbar-toggle",function(){var a=$(this);if(1==blackDashboard.misc.navbar_menu_visible)$html.removeClass("nav-open"),blackDashboard.misc.navbar_menu_visible=0,setTimeout(function(){a.removeClass("toggled"),$(".bodyClick").remove()},550);else{setTimeout(function(){a.addClass("toggled")},580);$('<div class="bodyClick"></div>').appendTo("body").click(function(){$html.removeClass("nav-open"),blackDashboard.misc.navbar_menu_visible=0,setTimeout(function(){a.removeClass("toggled"),$(".bodyClick").remove()},550)}),$html.addClass("nav-open"),blackDashboard.misc.navbar_menu_visible=1}}),$(window).resize(function(){if(seq=seq2=0,0==$full_screen_map.length&&0==$(".bd-docs").length){var a=$navbar.find('[data-toggle="collapse"]').attr("aria-expanded");$navbar.hasClass("bg-white")&&991<$(window).width()?$navbar.removeClass("bg-white").addClass("navbar-transparent"):$navbar.hasClass("navbar-transparent")&&$(window).width()<991&&"false"!=a&&$navbar.addClass("bg-white").removeClass("navbar-transparent")}}),blackDashboard={misc:{navbar_menu_visible:0},initMinimizeSidebar:function(){0!=$(".sidebar-mini").length&&(sidebar_mini_active=!0),$("#minimizeSidebar").click(function(){$(this);1==sidebar_mini_active?($("body").removeClass("sidebar-mini"),sidebar_mini_active=!1,blackDashboard.showSidebarMessage("Sidebar mini deactivated...")):($("body").addClass("sidebar-mini"),sidebar_mini_active=!0,blackDashboard.showSidebarMessage("Sidebar mini activated..."));var a=setInterval(function(){window.dispatchEvent(new Event("resize"))},180);setTimeout(function(){clearInterval(a)},1e3)})},showSidebarMessage:function(a){try{$.notify({icon:"tim-icons ui-1_bell-53",message:a},{type:"info",timer:4e3,placement:{from:"top",align:"right"}})}catch(a){console.log("Notify library is missing, please make sure you have the notifications library added.")}}};
|
||||||
//# sourceMappingURL=_site_dashboard_free/assets/js/dashboard-free.js.map
|
|
||||||
42
apps/static/assets/package.json
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
{
|
||||||
|
"name": "generic-compilation-script",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "Generic Builder",
|
||||||
|
"main": "gulpfile.js",
|
||||||
|
"author": "AppSeed",
|
||||||
|
"keywords": [
|
||||||
|
"css",
|
||||||
|
"sass",
|
||||||
|
"gulp",
|
||||||
|
"web"
|
||||||
|
],
|
||||||
|
"homepage": "https://appseed.us",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/app-generator"
|
||||||
|
},
|
||||||
|
"bugs": {
|
||||||
|
"email": "support@appseed.us"
|
||||||
|
},
|
||||||
|
"license": "MIT License",
|
||||||
|
"devDependencies": {
|
||||||
|
"browser-sync": "^2.27.4",
|
||||||
|
"del": "^6.0.0",
|
||||||
|
"gulp": "^4.0.2",
|
||||||
|
"gulp-autoprefixer": "^8.0.0",
|
||||||
|
"gulp-clean-css": "^4.3.0",
|
||||||
|
"gulp-cssbeautify": "^3.0.0",
|
||||||
|
"node-sass": "^6.0.1",
|
||||||
|
"gulp-file-include": "^2.3.0",
|
||||||
|
"gulp-header": "^2.0.9",
|
||||||
|
"gulp-htmlmin": "^5.0.1",
|
||||||
|
"gulp-npm-dist": "^1.0.3",
|
||||||
|
"gulp-plumber": "^1.2.1",
|
||||||
|
"gulp-rename": "^2.0.0",
|
||||||
|
"gulp-sass": "^5.0.0",
|
||||||
|
"gulp-sourcemaps": "^3.0.0",
|
||||||
|
"gulp-uglify": "^3.0.2",
|
||||||
|
"gulp-wait": "^0.0.2",
|
||||||
|
"merge-stream": "^2.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||