├── .gitignore ├── manage.py ├── project ├── __init__.py ├── config.py ├── models.py └── static │ ├── app.js │ ├── controllers.js │ ├── index.html │ ├── partials │ ├── home.html │ ├── login.html │ └── register.html │ └── services.js └── requirements.txt /.gitignore: -------------------------------------------------------------------------------- 1 | env 2 | venv 3 | __pycahe__ 4 | *.pyc 5 | *.sqlite 6 | .DS_Store 7 | env.sh 8 | blog.md 9 | migrations 10 | _old -------------------------------------------------------------------------------- /manage.py: -------------------------------------------------------------------------------- 1 | # manage.py 2 | 3 | from flask.ext.script import Manager 4 | from flask.ext.migrate import Migrate, MigrateCommand 5 | from project import app, db 6 | from project.models import User 7 | 8 | 9 | migrate = Migrate(app, db) 10 | manager = Manager(app) 11 | 12 | # migrations 13 | manager.add_command('db', MigrateCommand) 14 | 15 | 16 | @manager.command 17 | def create_db(): 18 | """Creates the db tables.""" 19 | db.create_all() 20 | 21 | 22 | @manager.command 23 | def drop_db(): 24 | """Drops the db tables.""" 25 | db.drop_all() 26 | 27 | 28 | @manager.command 29 | def create_admin(): 30 | """Creates the admin user.""" 31 | db.session.add(User(email='ad@min.com', password='admin', admin=True)) 32 | db.session.commit() 33 | 34 | 35 | @manager.command 36 | def create_data(): 37 | """Creates sample data.""" 38 | pass 39 | 40 | 41 | if __name__ == '__main__': 42 | manager.run() 43 | -------------------------------------------------------------------------------- /project/__init__.py: -------------------------------------------------------------------------------- 1 | # project/__init__.py 2 | 3 | 4 | from flask import Flask, request, jsonify, session 5 | from flask.ext.bcrypt import Bcrypt 6 | from flask.ext.sqlalchemy import SQLAlchemy 7 | from project.config import BaseConfig 8 | 9 | 10 | # config 11 | 12 | app = Flask(__name__) 13 | app.config.from_object(BaseConfig) 14 | 15 | bcrypt = Bcrypt(app) 16 | db = SQLAlchemy(app) 17 | 18 | from project.models import User 19 | 20 | 21 | # routes 22 | 23 | @app.route('/') 24 | def index(): 25 | return app.send_static_file('index.html') 26 | 27 | 28 | @app.route('/api/register', methods=['POST']) 29 | def register(): 30 | json_data = request.json 31 | user = User( 32 | email=json_data['email'], 33 | password=json_data['password'] 34 | ) 35 | try: 36 | db.session.add(user) 37 | db.session.commit() 38 | status = 'success' 39 | except: 40 | status = 'this user is already registered' 41 | db.session.close() 42 | return jsonify({'result': status}) 43 | 44 | 45 | @app.route('/api/login', methods=['POST']) 46 | def login(): 47 | json_data = request.json 48 | user = User.query.filter_by(email=json_data['email']).first() 49 | if user and bcrypt.check_password_hash( 50 | user.password, json_data['password']): 51 | session['logged_in'] = True 52 | status = True 53 | else: 54 | status = False 55 | return jsonify({'result': status}) 56 | 57 | 58 | @app.route('/api/logout') 59 | def logout(): 60 | session.pop('logged_in', None) 61 | return jsonify({'result': 'success'}) 62 | 63 | 64 | @app.route('/api/status') 65 | def status(): 66 | if session.get('logged_in'): 67 | if session['logged_in']: 68 | return jsonify({'status': True}) 69 | else: 70 | return jsonify({'status': False}) 71 | -------------------------------------------------------------------------------- /project/config.py: -------------------------------------------------------------------------------- 1 | # project/config.py 2 | 3 | import os 4 | basedir = os.path.abspath(os.path.dirname(__file__)) 5 | 6 | 7 | class BaseConfig(object): 8 | SECRET_KEY = 'my_precious' 9 | DEBUG = True 10 | BCRYPT_LOG_ROUNDS = 13 11 | SQLALCHEMY_DATABASE_URI = 'sqlite:///' + os.path.join(basedir, 'dev.sqlite') 12 | SQLALCHEMY_TRACK_MODIFICATIONS = False 13 | -------------------------------------------------------------------------------- /project/models.py: -------------------------------------------------------------------------------- 1 | # project/models.py 2 | 3 | 4 | import datetime 5 | from project import db, bcrypt 6 | 7 | 8 | class User(db.Model): 9 | 10 | __tablename__ = "users" 11 | 12 | id = db.Column(db.Integer, primary_key=True, autoincrement=True) 13 | email = db.Column(db.String(255), unique=True, nullable=False) 14 | password = db.Column(db.String(255), nullable=False) 15 | registered_on = db.Column(db.DateTime, nullable=False) 16 | admin = db.Column(db.Boolean, nullable=False, default=False) 17 | 18 | def __init__(self, email, password, admin=False): 19 | self.email = email 20 | self.password = bcrypt.generate_password_hash(password) 21 | self.registered_on = datetime.datetime.now() 22 | self.admin = admin 23 | 24 | def is_authenticated(self): 25 | return True 26 | 27 | def is_active(self): 28 | return True 29 | 30 | def is_anonymous(self): 31 | return False 32 | 33 | def get_id(self): 34 | return self.id 35 | 36 | def __repr__(self): 37 | return ''.format(self.email) 38 | -------------------------------------------------------------------------------- /project/static/app.js: -------------------------------------------------------------------------------- 1 | var myApp = angular.module('myApp', ['ngRoute']); 2 | 3 | myApp.config(function ($routeProvider) { 4 | $routeProvider 5 | .when('/', { 6 | templateUrl: 'static/partials/home.html', 7 | access: {restricted: true} 8 | }) 9 | .when('/login', { 10 | templateUrl: 'static/partials/login.html', 11 | controller: 'loginController', 12 | access: {restricted: false} 13 | }) 14 | .when('/logout', { 15 | controller: 'logoutController', 16 | access: {restricted: true} 17 | }) 18 | .when('/register', { 19 | templateUrl: 'static/partials/register.html', 20 | controller: 'registerController', 21 | access: {restricted: false} 22 | }) 23 | .when('/one', { 24 | template: '

This is page one!

', 25 | access: {restricted: true} 26 | }) 27 | .when('/two', { 28 | template: '

This is page two!

', 29 | access: {restricted: false} 30 | }) 31 | .otherwise({ 32 | redirectTo: '/' 33 | }); 34 | }); 35 | 36 | myApp.run(function ($rootScope, $location, $route, AuthService) { 37 | $rootScope.$on('$routeChangeStart', 38 | function (event, next, current) { 39 | AuthService.getUserStatus() 40 | .then(function(){ 41 | if (next.access.restricted && !AuthService.isLoggedIn()){ 42 | $location.path('/login'); 43 | $route.reload(); 44 | } 45 | }); 46 | }); 47 | }); -------------------------------------------------------------------------------- /project/static/controllers.js: -------------------------------------------------------------------------------- 1 | 2 | angular.module('myApp').controller('loginController', 3 | ['$scope', '$location', 'AuthService', 4 | function ($scope, $location, AuthService) { 5 | 6 | $scope.login = function () { 7 | 8 | // initial values 9 | $scope.error = false; 10 | $scope.disabled = true; 11 | 12 | // call login from service 13 | AuthService.login($scope.loginForm.email, $scope.loginForm.password) 14 | // handle success 15 | .then(function () { 16 | $location.path('/'); 17 | $scope.disabled = false; 18 | $scope.loginForm = {}; 19 | }) 20 | // handle error 21 | .catch(function () { 22 | $scope.error = true; 23 | $scope.errorMessage = "Invalid username and/or password"; 24 | $scope.disabled = false; 25 | $scope.loginForm = {}; 26 | }); 27 | 28 | }; 29 | 30 | }]); 31 | 32 | angular.module('myApp').controller('logoutController', 33 | ['$scope', '$location', 'AuthService', 34 | function ($scope, $location, AuthService) { 35 | 36 | $scope.logout = function () { 37 | 38 | // call logout from service 39 | AuthService.logout() 40 | .then(function () { 41 | $location.path('/login'); 42 | }); 43 | 44 | }; 45 | 46 | }]); 47 | 48 | angular.module('myApp').controller('registerController', 49 | ['$scope', '$location', 'AuthService', 50 | function ($scope, $location, AuthService) { 51 | 52 | $scope.register = function () { 53 | 54 | // initial values 55 | $scope.error = false; 56 | $scope.disabled = true; 57 | 58 | // call register from service 59 | AuthService.register($scope.registerForm.email, 60 | $scope.registerForm.password) 61 | // handle success 62 | .then(function () { 63 | $location.path('/login'); 64 | $scope.disabled = false; 65 | $scope.registerForm = {}; 66 | }) 67 | // handle error 68 | .catch(function () { 69 | $scope.error = true; 70 | $scope.errorMessage = "Something went wrong!"; 71 | $scope.disabled = false; 72 | $scope.registerForm = {}; 73 | }); 74 | 75 | }; 76 | 77 | }]); -------------------------------------------------------------------------------- /project/static/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Flask + Angular 6 | 7 | 8 | 9 | 10 |
11 |
12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /project/static/partials/home.html: -------------------------------------------------------------------------------- 1 |

Welcome!

2 | 3 |
4 | Logout 5 |
-------------------------------------------------------------------------------- /project/static/partials/login.html: -------------------------------------------------------------------------------- 1 |
2 |

Login

3 |
{{errorMessage}}
4 |
5 |
6 | 7 | 8 |
9 |
10 | 11 | 12 |
13 |
14 | 15 |
16 |
17 |
-------------------------------------------------------------------------------- /project/static/partials/register.html: -------------------------------------------------------------------------------- 1 |
2 |

Register

3 |
{{errorMessage}}
4 |
5 |
6 | 7 | 8 |
9 |
10 | 11 | 12 |
13 |
14 | 15 |
16 |
17 |
-------------------------------------------------------------------------------- /project/static/services.js: -------------------------------------------------------------------------------- 1 | angular.module('myApp').factory('AuthService', 2 | ['$q', '$timeout', '$http', 3 | function ($q, $timeout, $http) { 4 | 5 | // create user variable 6 | var user = null; 7 | 8 | // return available functions for use in controllers 9 | return ({ 10 | isLoggedIn: isLoggedIn, 11 | login: login, 12 | logout: logout, 13 | register: register, 14 | getUserStatus: getUserStatus 15 | }); 16 | 17 | function isLoggedIn() { 18 | if(user) { 19 | return true; 20 | } else { 21 | return false; 22 | } 23 | } 24 | 25 | function login(email, password) { 26 | 27 | // create a new instance of deferred 28 | var deferred = $q.defer(); 29 | 30 | // send a post request to the server 31 | $http.post('/api/login', {email: email, password: password}) 32 | // handle success 33 | .success(function (data, status) { 34 | if(status === 200 && data.result){ 35 | user = true; 36 | deferred.resolve(); 37 | } else { 38 | user = false; 39 | deferred.reject(); 40 | } 41 | }) 42 | // handle error 43 | .error(function (data) { 44 | user = false; 45 | deferred.reject(); 46 | }); 47 | 48 | // return promise object 49 | return deferred.promise; 50 | 51 | } 52 | 53 | function logout() { 54 | 55 | // create a new instance of deferred 56 | var deferred = $q.defer(); 57 | 58 | // send a get request to the server 59 | $http.get('/api/logout') 60 | // handle success 61 | .success(function (data) { 62 | user = false; 63 | deferred.resolve(); 64 | }) 65 | // handle error 66 | .error(function (data) { 67 | user = false; 68 | deferred.reject(); 69 | }); 70 | 71 | // return promise object 72 | return deferred.promise; 73 | 74 | } 75 | 76 | function register(email, password) { 77 | 78 | // create a new instance of deferred 79 | var deferred = $q.defer(); 80 | 81 | // send a post request to the server 82 | $http.post('/api/register', {email: email, password: password}) 83 | // handle success 84 | .success(function (data, status) { 85 | if(status === 200 && data.result){ 86 | deferred.resolve(); 87 | } else { 88 | deferred.reject(); 89 | } 90 | }) 91 | // handle error 92 | .error(function (data) { 93 | deferred.reject(); 94 | }); 95 | 96 | // return promise object 97 | return deferred.promise; 98 | 99 | } 100 | 101 | function getUserStatus() { 102 | return $http.get('/api/status') 103 | // handle success 104 | .success(function (data) { 105 | if(data.status){ 106 | user = true; 107 | } else { 108 | user = false; 109 | } 110 | }) 111 | // handle error 112 | .error(function (data) { 113 | user = false; 114 | }); 115 | } 116 | 117 | }]); -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | alembic==0.8.4 2 | bcrypt==2.0.0 3 | cffi==1.5.2 4 | Flask==0.10.1 5 | Flask-Bcrypt==0.7.1 6 | Flask-Migrate==1.8.0 7 | Flask-Script==2.0.5 8 | Flask-SQLAlchemy==2.1 9 | itsdangerous==0.24 10 | Jinja2==2.8 11 | Mako==1.0.3 12 | MarkupSafe==0.23 13 | pycparser==2.14 14 | python-bcrypt==0.3.1 15 | python-editor==0.5 16 | six==1.10.0 17 | SQLAlchemy==1.0.12 18 | Werkzeug==0.11.4 19 | --------------------------------------------------------------------------------