5 Commits

Author SHA1 Message Date
bc4d5dc6d9 Update README.md 2021-11-10 17:19:05 +02:00
295e1a78e6 Release v1.0.4 - Bump Codebase & Fixes 2021-11-10 17:17:55 +02:00
32f93f463e Bump Codebase Version 2021-11-10 17:17:23 +02:00
94e62b6ea9 CleanUP before Update 2021-11-10 17:03:57 +02:00
19f133a1ef Bump Codebase v1.0.6 2021-05-16 09:52:44 +03:00
235 changed files with 14507 additions and 21607 deletions

5
.gitignore vendored
View File

@ -26,3 +26,8 @@ _templates
# javascript
package-lock.json
.vscode/symbols.json
apps/static/assets/node_modules
apps/static/assets/yarn.lock
apps/static/assets/.temp

View File

@ -1,5 +1,26 @@
# 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
### Improvements

View File

@ -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 ./
COPY app app
# set environment variables
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"]

132
README.md
View File

@ -8,6 +8,8 @@
> 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/)**
- Flask Codebase - provided by **[AppSeed](https://appseed.us/)**
- SQLite, PostgreSQL, SQLAlchemy ORM
@ -22,6 +24,7 @@
## Table of Contents
* [Demo](#demo)
* [Docker Support](#docker-support)
* [Quick Start](#quick-start)
* [Documentation](#documentation)
* [File Structure](#file-structure)
@ -42,6 +45,27 @@
<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
> 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,34 +122,53 @@ Within the download you'll find the following directories and files:
```bash
< PROJECT ROOT >
|
|-- app/
| |-- home/ # Home Blueprint - serve app pages (private area)
| |-- base/ # Base Blueprint - handles the authentication
|-- apps/
| |
| |-- home/ # A simple app that serve HTML files
| | |-- routes.py # Define app routes
| |
| |-- authentication/ # Handles auth routes (login and register)
| | |-- routes.py # Define authentication routes
| | |-- models.py # Defines models
| | |-- forms.py # Define auth forms (login and register)
| |
| |-- static/
| | |-- <css, JS, images> # CSS files, Javascripts files
| |
| |-- templates/ # Templates used to render pages
| | |-- includes/ # HTML chunks and components
| | | |-- navigation.html # Top menu component
| | | |-- sidebar.html # Sidebar component
| | | |-- footer.html # App Footer
| | | |-- scripts.html # Scripts common to all pages
| | |
| | |-- 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
| |
| |-- includes/ #
| | |-- navigation.html # Top menu component
| | |-- sidebar.html # Sidebar component
| | |-- footer.html # App Footer
| | |-- scripts.html # Scripts common to all pages
| |
| |-- 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 # Registration page
| config.py # Set up the app
| __init__.py # Initialize the app
|
|-- requirements.txt # Development modules - SQLite storage
|-- requirements-mysql.txt # Production modules - Mysql DMBS
|-- requirements-pqsql.txt # Production modules - PostgreSql DMBS
|
|-- Dockerfile # Deployment
|-- docker-compose.yml # Deployment
|-- gunicorn-cfg.py # Deployment
|-- nginx # Deployment
| |-- appseed-app.conf # Deployment
|
|-- .env # Inject Configuration via Environment
|-- config.py # Set up the app
|-- run.py # Start the app - WSGI gateway
|
|-- ************************************************************************
@ -133,6 +176,61 @@ Within the download you'll find the following directories and files:
<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
At present, we officially aim to support the last two versions of the following browsers:

View File

@ -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()])

View File

@ -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

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -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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"}

View File

@ -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%);
}
}
}

View File

@ -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>
&nbsp; &nbsp;
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 %}

View File

@ -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>
&nbsp; &nbsp;
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 %}

View File

@ -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 -->

View File

@ -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

View File

@ -3,25 +3,27 @@
Copyright (c) 2019 - present AppSeed.us
"""
from flask import Flask, url_for
from flask import Flask
from flask_login import LoginManager
from flask_sqlalchemy import SQLAlchemy
from importlib import import_module
from logging import basicConfig, DEBUG, getLogger, StreamHandler
from os import path
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 ('base', 'home'):
module = import_module('app.{}.routes'.format(module_name))
for module_name in ('authentication', 'home'):
module = import_module('apps.{}.routes'.format(module_name))
app.register_blueprint(module.blueprint)
def configure_database(app):
@app.before_first_request
@ -32,8 +34,9 @@ def configure_database(app):
def shutdown_session(exception=None):
db.session.remove()
def create_app(config):
app = Flask(__name__, static_folder='base/static')
app = Flask(__name__)
app.config.from_object(config)
register_extensions(app)
register_blueprints(app)

View File

@ -6,9 +6,7 @@ Copyright (c) 2019 - present AppSeed.us
from flask import Blueprint
blueprint = Blueprint(
'base_blueprint',
'authentication_blueprint',
__name__,
url_prefix='',
template_folder='templates',
static_folder='static'
url_prefix=''
)

View 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()])

View File

@ -4,20 +4,19 @@ Copyright (c) 2019 - present AppSeed.us
"""
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)
username = Column(String, unique=True)
email = Column(String, unique=True)
password = Column(Binary)
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)
def __init__(self, **kwargs):
for property, value in kwargs.items():
@ -39,10 +38,11 @@ class User(db.Model, UserMixin):
@login_manager.user_loader
def user_loader(id):
return User.query.filter_by(id=id).first()
return Users.query.filter_by(id=id).first()
@login_manager.request_loader
def request_loader(request):
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

View 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

View File

@ -3,20 +3,26 @@
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/
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:]
@ -26,4 +32,3 @@ def verify_pass(provided_password, stored_password):
100000)
pwdhash = binascii.hexlify(pwdhash).decode('ascii')
return pwdhash == stored_password

View File

@ -17,6 +17,7 @@ class Config(object):
SQLALCHEMY_DATABASE_URI = 'sqlite:///' + os.path.join(basedir, 'db.sqlite3')
SQLALCHEMY_TRACK_MODIFICATIONS = False
class ProductionConfig(Config):
DEBUG = False
@ -35,9 +36,11 @@ class ProductionConfig(Config):
config('DB_NAME', default='appseed-flask')
)
class DebugConfig(Config):
DEBUG = True
# Load all possible configurations
config_dict = {
'Production': ProductionConfig,

View File

@ -8,7 +8,5 @@ from flask import Blueprint
blueprint = Blueprint(
'home_blueprint',
__name__,
url_prefix='',
template_folder='templates',
static_folder='static'
url_prefix=''
)

53
apps/home/routes.py Normal file
View 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

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View 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'));

View File

Before

Width:  |  Height:  |  Size: 1.6 MiB

After

Width:  |  Height:  |  Size: 1.6 MiB

View File

Before

Width:  |  Height:  |  Size: 37 KiB

After

Width:  |  Height:  |  Size: 37 KiB

View File

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 39 KiB

View File

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

Before

Width:  |  Height:  |  Size: 208 KiB

After

Width:  |  Height:  |  Size: 208 KiB

View File

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

Before

Width:  |  Height:  |  Size: 84 KiB

After

Width:  |  Height:  |  Size: 84 KiB

View File

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

Before

Width:  |  Height:  |  Size: 655 KiB

After

Width:  |  Height:  |  Size: 655 KiB

View File

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 38 KiB

View File

Before

Width:  |  Height:  |  Size: 73 KiB

After

Width:  |  Height:  |  Size: 73 KiB

View File

Before

Width:  |  Height:  |  Size: 75 KiB

After

Width:  |  Height:  |  Size: 75 KiB

View File

Before

Width:  |  Height:  |  Size: 8.0 KiB

After

Width:  |  Height:  |  Size: 8.0 KiB

View File

@ -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.")}}};
//# sourceMappingURL=_site_dashboard_free/assets/js/dashboard-free.js.map

View 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"
}
}

Some files were not shown because too many files have changed in this diff Show More