<repo_name>ZWhit196/experimental_server<file_sep>/helpers/Forms.py
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, validators

import database


class Register_Form(FlaskForm):
    username = StringField('Username',[validators.Length(min=3, max=30)])
    password = PasswordField('New Password', [
        validators.DataRequired(),
        validators.EqualTo('confirm', message='Passwords must match')
    ])
    confirm = PasswordField('<PASSWORD>')

    def validate(self):
        # run the normal validators
        rv = FlaskForm.validate(self)
        if not rv:
            return False
        # password validation checks
        if len(self.password.data) < 5: # Too short
            self.password.errors.append("The password must be 5 or more characters long, contain 1 number and 1 letter.")
            return False
        if self.password.data.isalpha(): # Only alpha char
            self.password.errors.append("The password must be 5 or more characters long, contain 1 number and 1 letter.")
            return False
        if self.password.data.isdigit(): # Only numerical
            self.password.errors.append("The password must be 5 or more characters long, contain 1 number and 1 letter.")
            return False
        if database.Username_used(self.username.data): # check if username taken
            self.email.errors.append('Email has already been registered.')
            return False
        return True
    
class Login_Form(FlaskForm):
    username = StringField('Username',[validators.Length(min=3, max=30)])
    password = PasswordField('<PASSWORD>', [
        validators.DataRequired(),
    ])

    def validate(self):
        # run the normal validators
        rv = FlaskForm.validate(self)
        if not rv:
            return False
        # password validation checks
        if len(self.password.data) < 5: # Too short
            self.password.errors.append("The password must be 5 or more characters long, contain 1 number and 1 letter.")
            return False
        if self.password.data.isalpha(): # Only alpha char
            self.password.errors.append("The password must be 5 or more characters long, contain 1 number and 1 letter.")
            return False
        if self.password.data.isdigit(): # Only numerical
            self.password.errors.append("The password must be 5 or more characters long, contain 1 number and 1 letter.")
            return False
        return True<file_sep>/views/tests.py
import os

from flask import Blueprint, request, Response, flash, jsonify, redirect
from flask_login import login_required
from flask.templating import render_template
from flask.helpers import url_for

from file_management.folder import FolderManager
from helpers import Error, Load_data

test_router = Blueprint('test', __name__, template_folder='templates')
SERVABLES = "static/servables/"

@test_router.route("/tests", methods=["GET"])
def home():
    filename = "example"
    url = SERVABLES+"vid/"+filename+'.mp4'
    return render_template("testing.html", url=url)


@test_router.route("/tests/folder")
@login_required
def make_base():
    FM = FolderManager()
    print(FM)
    print(FM.Get_urls())
#     print(FM.Create_personal_base())
    return "<p>Folder manager test</p>"



# @test_router.route("/tests/vid/<filename>")
def get_vid(filename):
    pass
#     url = SERVABLES+"vid/"+filename
#     print(url)
#     
#     def generate_stream(url):
#         with open(url) as vid:
# #             print(vid, dir(vid))
#             for l in vid.readlines():
#                 yield l
#     
#     return Response( generate_stream(url) )<file_sep>/configs.py
import os


def GenerateKey():
    return os.urandom(64)

DB_NAME = 'home_server'

DEV = {
    'CUSTOM_TEST_URL': True,
    'DEBUG': True,
#     'JS_TEST': True,
    'SECRET_KEY': 'key',
    'SQLALCHEMY_TRACK_MODIFICATIONS': False,
    'SQLALCHEMY_DATABASE_URI': 'sqlite:///'+DB_NAME+'.db',
    'WTF_CSRF_SECRET_KEY': 'key'
}

DEPLOY = {
    'CUSTOM_TEST_URL': False,
    'DEBUG': False,
#     'JS_TEST': False,
    'SECRET_KEY': GenerateKey(),
    'SQLALCHEMY_TRACK_MODIFICATIONS': False,
    'SQLALCHEMY_DATABASE_URI': 'sqlite:///'+DB_NAME+'.db',
    'WTF_CSRF_SECRET_KEY': GenerateKey()
}


def get_conf( c="DEPLOY" ):
    if c == "DEPLOY":
        return DEPLOY
    elif c == "DEV":
        return DEV
    else:
        return DEPLOY<file_sep>/views/front.py
from flask import Blueprint, request, Response, flash, jsonify
from flask.templating import render_template
from flask.helpers import url_for


front_router = Blueprint('front', __name__, template_folder='templates')


@front_router.route("/", methods=["GET"])
def home():
    return render_template("front/home.html")<file_sep>/templates/front/home.html
{% extends "base.html" %}

{% block includes %}
{% endblock %}

{% set title = 'Home' %}

{% block header %}
    {% include 'header.html'%}
{% endblock %}

{% block content %}
<div id="content" class="home">
    <div class="container">
        <div class="content">
            <h3>Home Placeholder</h3>
            {% if current_user.is_authenticated %}
                <p>You are logged in.</p>
                <ul>
                    <li><a href="/tests">testing page</a></li>
                    <li><a href="/tests/folder">test folder</a></li>
                </ul>
            {% else %}
                <p>You are NOT logged in. <a href="/login">Login here.</a></p>
            {% endif %}
        </div>
    </div>

</div>

{% endblock %}
<file_sep>/readme.txt
:EXPERIMENTAL SERVER STUFF:
So this is basically a server I'm using to experiment with stuff.
Probably not super useful for anything, but y'know, practise for better stuff, right?


:HANDY URLS I WANT TO HOLD ONTO FOR NOW:
https://blog.miguelgrinberg.com/post/flask-video-streaming-revisited
https://www.reddit.com/r/Python/comments/4go8n4/how_can_i_set_up_a_youtubestyle_video_server/?st=jdps4r6k&sh=69602a34
https://github.com/go2starr/py-flask-video-stream/blob/master/server.py<file_sep>/database/__init__.py
from database import DB, Querys

# DB.py
db = DB.db

# Querys.py
def Get_user(u):
    return Querys._Find_user(u)
def New_user(u, pw):
    return Querys._Create_user(u, pw)
def Username_used(u):
    return Querys._Check_username_used(u)
def Reset_password(u, pw):
    return Querys._Reset_user_password(pw, u)<file_sep>/home_server.py
import os
import traceback

from flask import Flask, request, jsonify
from flask.templating import render_template
from flask_login import LoginManager

from configs import get_conf, DB_NAME
from database import db
from models import User
from views import Routes

ERRORS = {400: "There was an issue with the request.", 403: "Access is denied.", 404: "This page/resource was not found.", 500: "An internal server error occured."}


def createNewKey():
    return os.urandom(64)

def setup_database(app):
    with app.app_context():
        db.create_all()

def create_app():
    app = Flask(__name__)
    
    conf = get_conf("DEV")
    for key in conf:
        app.config[key] = conf[key]
    
    db.init_app(app)
    
    # login manager
    login_manager = LoginManager()
    login_manager.init_app(app)
    login_manager.login_view = 'accounts.login'
    # keeps the user in the session
    @login_manager.user_loader
    def load_user(uid):
        return User.query.get(int(uid))
    
    @app.errorhandler(400)
    @app.errorhandler(403)
    @app.errorhandler(404)
    @app.errorhandler(500)
    def error_loading(ex):
        cd = 500
        if ex.code: 
            cd = ex.code
        msg = ERRORS[cd]
        if cd == 500:
            traceback.print_exc()
        if request.method == "POST":
            return Error(msg, status=ex.code)
        return render_template('error.html', err=cd, msg=msg)
    
    rs = Routes(True)
    for route in rs:
        app.register_blueprint( route )
    
    return app

def run_app():
    app = create_app()
    if not os.path.isfile(DB_NAME+'.db'):
        setup_database(app)
    app.run(host="0.0.0.0", port=5001)

if __name__ == "__main__":
    run_app()<file_sep>/views/accounts.py
import traceback

from flask import Blueprint, request, Response, flash, jsonify, abort, redirect
from flask_login import login_user, logout_user, current_user, login_required
from flask.templating import render_template
from flask.helpers import url_for

from database import Username_used, New_user, Get_user
import helpers
# import


accounts_router = Blueprint('accounts', __name__, template_folder='templates')


@accounts_router.route("/login", methods=["GET","POST"])
def login():
    form = helpers.Login_form()
    if request.method == 'GET':
        return render_template('accounts/login.html', form=form)
    if form.validate_on_submit(): # Basically the `POST` request
        username = form.username.data.lower()
        password = form.password.data
        registered_user = Get_user(username)
        if registered_user is not None:
            login_user(registered_user, remember=True)
            flash("You are logged in.")
            return redirect(url_for("front.home"))
    flash("Username or password is invalid.")
    return redirect(url_for("accounts.login"))


@accounts_router.route("/logout", methods=["GET","POST"])
def logout():
    logout_user()
    flash("You have been logged out.")
    return redirect(url_for('front.home'))


@accounts_router.route("/create", methods=["GET","POST"])
def create():
    try:
        form = helpers.Reg_form()
        if form.validate_on_submit(): # Basically the `POST` request
            taken = Username_used(form.username.data.lower())
            if not taken:
                user = New_user( form.username.data.lower(), form.password.data )
                login_user(user, remember=True)
                
                # Setup folder and update reg.json
#                 FM.Create_personal_base() # Create your personal folder
                
                flash("User has been created. Welcome!")
                return redirect(url_for("front.home"))
            flash("That username has already been taken.")
            return redirect(url_for("accounts.create"))
        if form.username.errors or form.password.errors:
            for err in form.username.errors:
                flash(err)
            for err in form.password.errors:
                flash(err)
            return redirect(url_for("accounts.create"))
        return render_template("accounts/create.html", form=form)
    except Exception as e:
        traceback.print_exc()
        print("Error in create:",e)
        if request.method == "POST":
            return helpers.Error(error="SERVER", status=500)
        abort(500)<file_sep>/views/__init__.py
'''
    Register the views here.
'''
from views.front import front_router
from views.accounts import accounts_router
from views.tests import test_router


routes = [
    front_router,
    accounts_router
]

def Routes(dev=False):
    l = list(routes)
    if dev:
        l.extend( [test_router] )
    return l<file_sep>/helpers/Request_handling.py
import json

def _Request_data(rd):
    rd = rd.decode("utf-8")
    return json.loads(rd)<file_sep>/helpers/__init__.py
from helpers import Forms, Error_helper, Request_handling


# WTForms
def Reg_form():
    return Forms.Register_Form()
def Login_form():
    return Forms.Login_Form()

# Request data
def Load_data(r):
    return Request_handling._Request_data(r)

# Error_helper
def Error(content="Error occured", error='REQUEST', status=400):
    return Error_helper._Get_error(content, error, status)<file_sep>/models.py
import datetime
from passlib.hash import pbkdf2_sha512

from database.DB import db


class User(db.Model):
    uid = db.Column('uid', db.Integer, primary_key=True, nullable=False)
    username = db.Column(db.String(256), unique=True, nullable=False)
    password = db.Column(db.Text, nullable=False)
    account_created = db.Column(db.DateTime())
    last_login = db.Column(db.DateTime())

    def __init__(self, username, password):
        self.username = username.lower()
        self.set_password(password)
        self.account_created = datetime.datetime.now()
        self.last_login = datetime.datetime.now()

    def __repr__(self):
        return '<User: {}>'.format(self.username)

    # functions for login manager
    def is_authenticated(self):
        return True

    def is_active(self):
        return True

    def is_anonymous(self):
        return False

    def get_id(self):
        return str(self.uid)
    # End login manager func.
    
    def commit_self(self):
        db.session.add(self)
        db.session.commit()
    
    def set_password(self, password):
        ''' hashes and salts a new password '''
        self.password = <PASSWORD>.encrypt(password, rounds=200000, salt_size=16)

    def verify_password(self, password):
        ''' verifies a password '''
        return pbkdf2_sha512.verify(password, self.password)

    def update_password(self, password):
        ''' sets and saves a password '''
        self.set_password(password)
        self.commit_self()

    def update_login(self):
        ''' updates time since last login '''
        self.last_login = datetime.datetime.now()
        self.commit_self()<file_sep>/file_management/files.py
'''
    This is intended for managing the files of a user.
    Adding, removing, moving, and retrieving names handled here.
'''
from flask_login import current_user<file_sep>/file_management/folder.py
'''
    Folder management, i.e. creating/removing folders and updating reg.json,
    is performed here.
'''
import os

from flask_login import current_user


class FolderManager(object):
    '''
        Class for object management.
    '''
    base_url = "./static/servables/"
    shared_video = "./static/servables/vid/"
    shared_music = "./static/servables/music/"
    music_url = ""
    video_url = ""
    
    def __init__(self):
        self.base_url += current_user.username + "/"
        self.music_url = self.base_url + "music/"
        self.video_url = self.base_url + "video/"
        
    def __repr__(self):
        return "<Folder: {}>".format(self.base_url)
    
    def Get_urls(self):
        URLS = {
                "BASE": self.base_url,
                "SHARED MUSIC": self.shared_music,
                "SHARED VIDEO": self.shared_video
        }
        return URLS
    
    # Create
    def Create_personal_base(self):
        '''
            Creates the base personal folder for a user.
        '''
        try:
            os.makedirs(self.base_url, exist_ok=True)
            return True
        except Exception as e:
            print("Create error:",e)
        
        raise NotImplementedError
    
    def Add_personal_subfolder(self, name):
        raise NotImplementedError
    
    # Retrieve
    def Retrieve_folder_list(self):
        raise NotImplementedError
    
    # Remove
    def Remove_folder(self):
        raise NotImplementedError<file_sep>/static/js/utils.js
var ajaxobj = null, callback = null, errorback = null, showncol = null;


function SetAjaxArgs(o, c, e) { ajaxobj = o; callback = c; errorback = e; }

function NullAjaxArgs() { ajaxobj = null; callback = null; errorback = null; }

function AjaxCall() {
    $.ajax({
        url: window.location,
        method: 'POST',
        data: JSON.stringify( ajaxobj ),
        contentType: 'application/json'
    }).done(function(dt) {
        callback(dt);
        NullAjaxArgs();
    }).fail(function(e) {
        console.log("Failed processing", e.status, e.responseJSON.Message);
        alert("Data Request Error:  "+String(e.status)+"\n"+e.responseJSON.Message);
        errorback();
        NullAjaxArgs();
    });
}

function isInArray(target, array) {
    if( array.indexOf(target)>-1 ) return true;
    return false; 
}

function ToggleLoader(id, l_id) {
    if (!l_id) l_id = ".loader";
    if (typeof id == "object" && id.length > 0) for (var i=0; i<id.length; i++) $( id[i] ).find(l_id).toggle();
    else if (typeof id == "string") $(id).find(l_id).toggle();
}

function copyToClipboard(element) {
    var $temp = $("<input>");
    $("body").append($temp);
    $temp.val($(element).text()).select();
    document.execCommand("copy");
    $temp.remove();
}

function togglePassField(el) {
    // Toggle readability of password field given, switch between hidden text and readable text.
    if ( $( el ).prop("type") == "password" ) {
        $( el ).prop("type", "text");
        $( el ).prop("placeholder", "password");
    } else {
        $( el ).prop("type", "password");
        $( el ).prop("placeholder", "********");
    }
}<file_sep>/file_management/youtuber.py
'''
    Experimenting with pytube will be done here, 
    otherwise this will be a manual download system
    of vid.
'''<file_sep>/database/Querys.py
from database.DB import db
from models import User

def _Create_user(username, password):
    ''' Create a user and commit it to the database. '''
    u = User(username, password)
    db.session.add(u)
    db.session.commit()
    return u

def _Find_user(username):
    ''' Find a user by email. '''
    return User.query.filter_by(username=username).first()

def _Check_username_used(username):
    ''' Checks whether an email is used. '''
    if User.query.filter_by(username=username).first():
        return True
    return False

def _Reset_user_password(password, username):
    ''' Reset password. '''
    u = User.query.filter_by(username=username).first()
    if u:
        u.update_password(password)
        return True
    return False