From b6de8eef5266132ac83b4cb170f168cdd9b97f04 Mon Sep 17 00:00:00 2001 From: Xevion Date: Sat, 29 Jun 2019 16:31:30 -0500 Subject: [PATCH] adding all previous files for init commit --- app/__init__.py | 18 + app/forms.py | 9 + app/models.py | 34 + app/routes.py | 73 + app/static/brand.png | Bin 0 -> 10228 bytes app/static/bulma.css | 6610 ++++++++++++++++++++++++++++ app/static/particlesjs-config.json | 110 + app/static/style.css | 77 + app/templates/base.html | 85 + app/templates/index.html | 56 + app/templates/login.html | 86 + app/templates/signup.html | 75 + config.py | 9 + wsgi.py | 6 + 14 files changed, 7248 insertions(+) create mode 100644 app/__init__.py create mode 100644 app/forms.py create mode 100644 app/models.py create mode 100644 app/routes.py create mode 100644 app/static/brand.png create mode 100644 app/static/bulma.css create mode 100644 app/static/particlesjs-config.json create mode 100644 app/static/style.css create mode 100644 app/templates/base.html create mode 100644 app/templates/index.html create mode 100644 app/templates/login.html create mode 100644 app/templates/signup.html create mode 100644 config.py create mode 100644 wsgi.py diff --git a/app/__init__.py b/app/__init__.py new file mode 100644 index 0000000..71b02a7 --- /dev/null +++ b/app/__init__.py @@ -0,0 +1,18 @@ +# Main Flask and Flask configs +from flask import Flask +from config import Config +# Flask Extensions +from flask_sqlalchemy import SQLAlchemy +from flask_migrate import Migrate +from flask_login import LoginManager + +# App & App config setup +app = Flask(__name__) +app.config.from_object(Config) +# App extension setup +login = LoginManager(app) +login.login_view = 'login' +db = SQLAlchemy(app) +migrate = Migrate(app, db) + +from app import routes, models \ No newline at end of file diff --git a/app/forms.py b/app/forms.py new file mode 100644 index 0000000..91ea3d1 --- /dev/null +++ b/app/forms.py @@ -0,0 +1,9 @@ +from flask_wtf import FlaskForm +from wtforms import StringField, PasswordField, BooleanField, SubmitField +from wtforms.validators import DataRequired + +class LoginForm(FlaskForm): + username = StringField('Username', validators=[DataRequired()]) + password = PasswordField('Password', validators=[DataRequired()]) + remember_me = BooleanField('Remember Me') + submit = SubmitField('Sign in') \ No newline at end of file diff --git a/app/models.py b/app/models.py new file mode 100644 index 0000000..4f12207 --- /dev/null +++ b/app/models.py @@ -0,0 +1,34 @@ +from flask_login import UserMixin +from datetime import datetime +from app import db, login +from werkzeug.security import generate_password_hash, check_password_hash + +@login.user_loader +class User(UserMixin, db.Model): + id = db.Column(db.Integer, primary_key=True) + username = db.Column(db.String(64), index=True, unique=True) + email = db.Column(db.String(120), index=True, unique=True) + password_hash = db.Column(db.String(64)) + posts = db.relationship('Post', backref='author', lazy='dynamic') + + def set_password(self, password): + self.password_hash = generate_password_hash(password) + + def check_password(self, password): + return check_password_hash(self.password_hash, password) + + def __repr__(self): + return ''.format(self.username) + +class Post(db.Model): + id = db.Column(db.Integer, primary_key=True) + body = db.Column(db.String(140)) + timestamp = db.Column(db.DateTime, index=True, default=datetime.utcnow) + user_id = db.Column(db.Integer, db.ForeignKey('user.id')) + + def __repr__(self): + return ''.format(self.body) + +@login.user_loader +def load_user(id): + return User.query.get(int(id)) \ No newline at end of file diff --git a/app/routes.py b/app/routes.py new file mode 100644 index 0000000..d503933 --- /dev/null +++ b/app/routes.py @@ -0,0 +1,73 @@ +from app import app +from app.models import User +from app.forms import LoginForm +from werkzeug.urls import url_parse +from flask import render_template, redirect, url_for, flash, request, jsonify +from flask_login import current_user, login_user, logout_user, login_required +import random +import string +import faker +import json + +fake = faker.Faker() + +def strgen(length): return ''.join( + random.choices(list(string.ascii_letters), k=length)) + + +@app.route('/dashboard') +def dashboard(): + return '' + +@app.route('/userinfo') +@login_required +def user_info(): + prepare = { + 'id' : current_user.get_id(), + 'email' : current_user.email, + 'username' : current_user.username, + 'password_hash' : current_user.password_hash, + 'is_active' : current_user.is_active, + 'is_anonymous' : current_user.is_anonymous, + 'is_authenticated' : current_user.is_authenticated, + 'metadata' : current_user.metadata.info + } + return jsonify(prepare) + +@app.route('/') +def index(): + if current_user.is_authenticated: + print(current_user) + content = [{'text': fake.paragraph(nb_sentences=15), + 'seed': random.randint(0, 1000), + 'title': fake.word().title()} + for _ in range(10)] + return render_template('index.html', content=content) + +@app.route('/signup') +@app.route('/sign-up') +def signup(): + return render_template('signup.html', title='Sign Up', hideSignup=True) + + +@app.route('/login', methods=['GET', 'POST']) +def login(): + if current_user.is_authenticated: + return redirect(url_for('index')) + form = LoginForm() + if form.validate_on_submit(): + user = User.query.filter_by(username=form.username.data).first() + if user is None or not user.check_password(form.password.data): + flash('Invalid username or password') + return redirect(url_for('login')) + login_user(user, remember=form.remember_me.data) + next_page = request.args.get('next') + if not next_page or url_parse(next_page).netloc != '': + next_page = url_for('index') + return redirect(next_page) + return render_template('login.html', title='Sign In', form=form, hideLogin=True) + +@app.route('/logout') +def logout(): + logout_user() + return redirect(url_for('index')) diff --git a/app/static/brand.png b/app/static/brand.png new file mode 100644 index 0000000000000000000000000000000000000000..3b59945a33ae04d71482d0c772d935c6ea100038 GIT binary patch literal 10228 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