Comenzando a utilizar SQLAlchemy
Tuve que reescribir bastante para lograrlo, pero ya funciona :3
This commit is contained in:
23
.idea/dataSources.xml
generated
Normal file
23
.idea/dataSources.xml
generated
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="DataSourceManagerImpl" format="xml" multifile-model="true">
|
||||||
|
<data-source source="LOCAL" name="files" uuid="630cc1a9-f960-489a-9dd4-5eb988f04598">
|
||||||
|
<driver-ref>sqlite.xerial</driver-ref>
|
||||||
|
<synchronize>true</synchronize>
|
||||||
|
<jdbc-driver>org.sqlite.JDBC</jdbc-driver>
|
||||||
|
<jdbc-url>jdbc:sqlite:$PROJECT_DIR$/instance/files.sqlite</jdbc-url>
|
||||||
|
<driver-properties>
|
||||||
|
<property name="enable_load_extension" value="true" />
|
||||||
|
</driver-properties>
|
||||||
|
</data-source>
|
||||||
|
<data-source source="LOCAL" name="files-test" uuid="8b911962-ca9f-4f64-973c-88617733acd2">
|
||||||
|
<driver-ref>sqlite.xerial</driver-ref>
|
||||||
|
<synchronize>true</synchronize>
|
||||||
|
<jdbc-driver>org.sqlite.JDBC</jdbc-driver>
|
||||||
|
<jdbc-url>jdbc:sqlite:$PROJECT_DIR$/instance/files-test.sqlite</jdbc-url>
|
||||||
|
<driver-properties>
|
||||||
|
<property name="enable_load_extension" value="true" />
|
||||||
|
</driver-properties>
|
||||||
|
</data-source>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
4
.idea/encodings.xml
generated
Normal file
4
.idea/encodings.xml
generated
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="Encoding" addBOMForNewFiles="with NO BOM" />
|
||||||
|
</project>
|
||||||
19
.idea/files.danielcortes.xyz.iml
generated
Normal file
19
.idea/files.danielcortes.xyz.iml
generated
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module type="PYTHON_MODULE" version="4">
|
||||||
|
<component name="NewModuleRootManager">
|
||||||
|
<content url="file://$MODULE_DIR$" />
|
||||||
|
<orderEntry type="inheritedJdk" />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
</component>
|
||||||
|
<component name="TemplatesService">
|
||||||
|
<option name="TEMPLATE_CONFIGURATION" value="Jinja2" />
|
||||||
|
<option name="TEMPLATE_FOLDERS">
|
||||||
|
<list>
|
||||||
|
<option value="$MODULE_DIR$/files/templates" />
|
||||||
|
</list>
|
||||||
|
</option>
|
||||||
|
</component>
|
||||||
|
<component name="TestRunnerService">
|
||||||
|
<option name="PROJECT_TEST_RUNNER" value="Unittests" />
|
||||||
|
</component>
|
||||||
|
</module>
|
||||||
7
.idea/misc.xml
generated
Normal file
7
.idea/misc.xml
generated
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="JavaScriptSettings">
|
||||||
|
<option name="languageLevel" value="ES6" />
|
||||||
|
</component>
|
||||||
|
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.7 (files.danielcortes.xyz)" project-jdk-type="Python SDK" />
|
||||||
|
</project>
|
||||||
8
.idea/modules.xml
generated
Normal file
8
.idea/modules.xml
generated
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectModuleManager">
|
||||||
|
<modules>
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/.idea/files.danielcortes.xyz.iml" filepath="$PROJECT_DIR$/.idea/files.danielcortes.xyz.iml" />
|
||||||
|
</modules>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
6
.idea/vcs.xml
generated
Normal file
6
.idea/vcs.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
@@ -3,42 +3,45 @@ import os
|
|||||||
from flask import Flask, render_template
|
from flask import Flask, render_template
|
||||||
from werkzeug import SharedDataMiddleware
|
from werkzeug import SharedDataMiddleware
|
||||||
|
|
||||||
|
|
||||||
def create_app():
|
def create_app():
|
||||||
app = Flask(__name__, instance_relative_config=True)
|
app = Flask(__name__, instance_relative_config=True)
|
||||||
|
|
||||||
app.config.from_mapping(
|
app.config.from_mapping(
|
||||||
DATABASE = os.path.join(app.instance_path, 'files.sqlite'),
|
SQLALCHEMY_DATABASE_URI="sqlite:///{}".format(os.path.join(app.instance_path, 'files.sqlite')),
|
||||||
USERNAME = 'dev',
|
SQLALCHEMY_TRACK_MODIFICATIONS=False,
|
||||||
PASSWORD = 'secret',
|
USERNAME='dev',
|
||||||
SECRET_KEY = '1337',
|
PASSWORD='secret',
|
||||||
UPLOAD_FOLDER = 'uploads',
|
SECRET_KEY='1337',
|
||||||
ALLOWED_EXTENSIONS = set(['png', 'jpg'])
|
UPLOAD_FOLDER='uploads'
|
||||||
)
|
)
|
||||||
|
|
||||||
app.config.from_pyfile('config.py')
|
app.config.from_pyfile('config.py')
|
||||||
app.wsgi_app = SharedDataMiddleware(app.wsgi_app, { '/uploads': app.config['UPLOAD_FOLDER'] })
|
app.wsgi_app = SharedDataMiddleware(app.wsgi_app, {'/uploads': app.config['UPLOAD_FOLDER']})
|
||||||
|
|
||||||
try:
|
try:
|
||||||
os.makedirs(app.instance_path)
|
os.makedirs(app.instance_path)
|
||||||
except OSError:
|
except OSError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
from . import db
|
from files.models import db
|
||||||
db.init_app(app)
|
db.init_app(app)
|
||||||
|
|
||||||
from . import auth
|
from files import commands
|
||||||
|
commands.init_app(app)
|
||||||
|
|
||||||
|
from files import auth
|
||||||
app.register_blueprint(auth.bp)
|
app.register_blueprint(auth.bp)
|
||||||
|
|
||||||
from . import categories
|
from files import categories
|
||||||
app.register_blueprint(categories.bp)
|
app.register_blueprint(categories.bp)
|
||||||
|
|
||||||
from . import about
|
from files import about
|
||||||
app.register_blueprint(about.bp)
|
app.register_blueprint(about.bp)
|
||||||
app.add_url_rule('/about', endpoint='about')
|
app.add_url_rule('/about', endpoint='about')
|
||||||
|
|
||||||
from . import files
|
from files import files
|
||||||
app.register_blueprint(files.bp)
|
app.register_blueprint(files.bp)
|
||||||
app.add_url_rule('/', endpoint='index')
|
app.add_url_rule('/', endpoint='index')
|
||||||
|
|
||||||
|
|
||||||
return app
|
return app
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
from files.db import get_db
|
|
||||||
|
|
||||||
from flask import Blueprint, request, render_template, redirect, url_for, flash
|
from flask import Blueprint, request, render_template, redirect, url_for, flash
|
||||||
|
from files.models import db, Message
|
||||||
|
|
||||||
bp = Blueprint('about', __name__, url_prefix='/about')
|
bp = Blueprint('about', __name__, url_prefix='/about')
|
||||||
|
|
||||||
|
|
||||||
@bp.route('/', methods=('GET', 'POST'))
|
@bp.route('/', methods=('GET', 'POST'))
|
||||||
def about():
|
def about():
|
||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
@@ -11,7 +11,6 @@ def about():
|
|||||||
email = request.form['email']
|
email = request.form['email']
|
||||||
message = request.form['message']
|
message = request.form['message']
|
||||||
|
|
||||||
db = get_db()
|
|
||||||
error = None
|
error = None
|
||||||
|
|
||||||
if not name:
|
if not name:
|
||||||
@@ -20,21 +19,18 @@ def about():
|
|||||||
error = 'Missing email'
|
error = 'Missing email'
|
||||||
elif not message:
|
elif not message:
|
||||||
error = 'Empty message'
|
error = 'Empty message'
|
||||||
|
|
||||||
if error is not None:
|
if error is not None:
|
||||||
flash(error)
|
flash(error)
|
||||||
else:
|
else:
|
||||||
db.execute(
|
m = Message(name, email, message)
|
||||||
'INSERT INTO messages'
|
db.session.add(m)
|
||||||
' (name, email, message)'
|
db.session.commit()
|
||||||
' VALUES (?, ?, ?)',
|
return redirect(url_for('about.thanks'))
|
||||||
(name, email, message)
|
|
||||||
)
|
|
||||||
db.commit()
|
|
||||||
return redirect(url_for('about.thanks'))
|
|
||||||
|
|
||||||
return render_template('about/about.html')
|
return render_template('about/about.html')
|
||||||
|
|
||||||
|
|
||||||
@bp.route('/thanks')
|
@bp.route('/thanks')
|
||||||
def thanks():
|
def thanks():
|
||||||
return render_template('about/thanks.html')
|
return render_template('about/thanks.html')
|
||||||
|
|||||||
@@ -1,47 +1,46 @@
|
|||||||
import functools
|
import functools
|
||||||
|
|
||||||
from files.db import get_db
|
from files.models import db, User
|
||||||
|
|
||||||
from werkzeug.exceptions import abort
|
from werkzeug.exceptions import abort
|
||||||
from werkzeug.security import check_password_hash
|
from werkzeug.security import check_password_hash
|
||||||
|
|
||||||
from flask import Blueprint, request, flash, render_template, session, g, redirect, url_for
|
from flask import Blueprint, request, flash, render_template, session, g, redirect, url_for
|
||||||
|
|
||||||
|
|
||||||
bp = Blueprint('auth', __name__, url_prefix='/auth')
|
bp = Blueprint('auth', __name__, url_prefix='/auth')
|
||||||
|
|
||||||
|
|
||||||
@bp.route('/login', methods=('GET', 'POST'))
|
@bp.route('/login', methods=('GET', 'POST'))
|
||||||
def login():
|
def login():
|
||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
username = request.form['username']
|
username = request.form['username']
|
||||||
password = request.form['password']
|
password = request.form['password']
|
||||||
|
|
||||||
db = get_db()
|
|
||||||
error = None
|
error = None
|
||||||
|
|
||||||
user = db.execute(
|
user = User.query.filter_by(username=username).first()
|
||||||
'SELECT * FROM users WHERE username = ?', (username,)
|
|
||||||
).fetchone()
|
|
||||||
|
|
||||||
if user is None:
|
if user is None:
|
||||||
error = 'Incorrect username.'
|
error = 'Incorrect username.'
|
||||||
elif not check_password_hash(user['password'], password):
|
elif not check_password_hash(user.password, password):
|
||||||
error = 'Incorrect password.'
|
error = 'Incorrect password.'
|
||||||
|
|
||||||
if error is None:
|
if error is None:
|
||||||
session.clear()
|
session.clear()
|
||||||
session['user_id'] = user['id']
|
session['user_id'] = user.id
|
||||||
return redirect(url_for('index'))
|
return redirect(url_for('index'))
|
||||||
|
|
||||||
flash(error)
|
flash(error)
|
||||||
|
|
||||||
return render_template('auth/login.html')
|
return render_template('auth/login.html')
|
||||||
|
|
||||||
|
|
||||||
@bp.route('/logout')
|
@bp.route('/logout')
|
||||||
def logout():
|
def logout():
|
||||||
session.clear()
|
session.clear()
|
||||||
return redirect(url_for('index'))
|
return redirect(url_for('index'))
|
||||||
|
|
||||||
|
|
||||||
@bp.before_app_request
|
@bp.before_app_request
|
||||||
def load_logged_in_user():
|
def load_logged_in_user():
|
||||||
user_id = session.get('user_id')
|
user_id = session.get('user_id')
|
||||||
@@ -49,9 +48,7 @@ def load_logged_in_user():
|
|||||||
if user_id is None:
|
if user_id is None:
|
||||||
g.user = None
|
g.user = None
|
||||||
else:
|
else:
|
||||||
g.user = get_db().execute(
|
g.user = User.query.get(user_id)
|
||||||
'SELECT * FROM users WHERE id = ?', (user_id,)
|
|
||||||
).fetchone()
|
|
||||||
|
|
||||||
|
|
||||||
def admin_required(view):
|
def admin_required(view):
|
||||||
@@ -63,4 +60,3 @@ def admin_required(view):
|
|||||||
return view(**kwargs)
|
return view(**kwargs)
|
||||||
|
|
||||||
return wrapped_view
|
return wrapped_view
|
||||||
|
|
||||||
|
|||||||
@@ -1,48 +1,15 @@
|
|||||||
from flask import Flask, Blueprint, flash, request, redirect, url_for, current_app, render_template, jsonify
|
from flask import Flask, Blueprint, flash, request, redirect, url_for, current_app, render_template, jsonify
|
||||||
|
|
||||||
from werkzeug.exceptions import abort
|
|
||||||
|
|
||||||
from files.db import get_db
|
|
||||||
from files.auth import admin_required
|
from files.auth import admin_required
|
||||||
from files.files import get_files_by_category
|
from files.models import Category, db
|
||||||
|
|
||||||
bp = Blueprint('categories', __name__, url_prefix='/categories')
|
bp = Blueprint('categories', __name__, url_prefix='/categories')
|
||||||
|
|
||||||
def get_categories():
|
|
||||||
db = get_db()
|
|
||||||
categories = db.execute(
|
|
||||||
'SELECT id, name'
|
|
||||||
' FROM categories'
|
|
||||||
' ORDER BY name DESC'
|
|
||||||
).fetchall()
|
|
||||||
return categories
|
|
||||||
|
|
||||||
def get_category(id):
|
|
||||||
db = get_db()
|
|
||||||
category = db.execute(
|
|
||||||
'SELECT id, name'
|
|
||||||
' FROM categories'
|
|
||||||
' WHERE id = ?'
|
|
||||||
' LIMIT 1',
|
|
||||||
(id,)
|
|
||||||
).fetchone()
|
|
||||||
return category
|
|
||||||
|
|
||||||
def get_files_by_category(id):
|
|
||||||
db = get_db()
|
|
||||||
files = db.execute(
|
|
||||||
'SELECT id, filename, private'
|
|
||||||
' FROM files'
|
|
||||||
' WHERE category = ?'
|
|
||||||
' ORDER BY filename DESC',
|
|
||||||
(id,)
|
|
||||||
).fetchall()
|
|
||||||
return files
|
|
||||||
|
|
||||||
@bp.route('/')
|
@bp.route('/')
|
||||||
def index():
|
def index():
|
||||||
categories = get_categories()
|
categories = Category.query.all()
|
||||||
return render_template('categories/index.html', categories=categories)
|
return render_template('categories/index.html', categories=categories)
|
||||||
|
|
||||||
|
|
||||||
@bp.route('/create', methods=['GET', 'POST'])
|
@bp.route('/create', methods=['GET', 'POST'])
|
||||||
@admin_required
|
@admin_required
|
||||||
@@ -57,21 +24,18 @@ def create():
|
|||||||
if error is not None:
|
if error is not None:
|
||||||
flash(error)
|
flash(error)
|
||||||
else:
|
else:
|
||||||
db = get_db()
|
c = Category(name)
|
||||||
db.execute(
|
db.session.add(c)
|
||||||
'INSERT INTO categories (name)'
|
db.session.commit()
|
||||||
' VALUES (?)',
|
|
||||||
(name,)
|
|
||||||
)
|
|
||||||
db.commit()
|
|
||||||
return redirect(url_for('categories.index'))
|
return redirect(url_for('categories.index'))
|
||||||
|
|
||||||
return render_template('categories/create.html')
|
return render_template('categories/create.html')
|
||||||
|
|
||||||
|
|
||||||
@bp.route('<int:id>')
|
@bp.route('<int:id>')
|
||||||
def view(id):
|
def view(id):
|
||||||
category = get_category(id)
|
category = Category.query.get(id)
|
||||||
files = get_files_by_category(id)
|
files = category.files
|
||||||
return render_template('categories/view.html', category=category, files=files)
|
return render_template('categories/view.html', category=category, files=files)
|
||||||
|
|
||||||
|
|
||||||
@@ -80,9 +44,8 @@ def view(id):
|
|||||||
def update():
|
def update():
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
@bp.route('/<int:id>/delete')
|
@bp.route('/<int:id>/delete')
|
||||||
@admin_required
|
@admin_required
|
||||||
def delete():
|
def delete():
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
90
files/commands.py
Normal file
90
files/commands.py
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
import click
|
||||||
|
import os
|
||||||
|
|
||||||
|
from flask import current_app
|
||||||
|
from flask.cli import with_appcontext
|
||||||
|
|
||||||
|
from werkzeug.security import generate_password_hash
|
||||||
|
|
||||||
|
from files.models import db, User, Category, File
|
||||||
|
|
||||||
|
|
||||||
|
def init_db():
|
||||||
|
db.drop_all()
|
||||||
|
db.create_all()
|
||||||
|
|
||||||
|
|
||||||
|
def generate_admin():
|
||||||
|
username = current_app.config['USERNAME']
|
||||||
|
password = current_app.config['PASSWORD']
|
||||||
|
|
||||||
|
u = User(username, generate_password_hash(password))
|
||||||
|
db.session.add(u)
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
|
||||||
|
def add_defaults():
|
||||||
|
default_category = Category('Default')
|
||||||
|
db.session.add(default_category)
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
|
||||||
|
def add_files():
|
||||||
|
existing_files = os.listdir(current_app.config['UPLOAD_FOLDER'])
|
||||||
|
default_category = Category.query.filter_by(name='Default').first()
|
||||||
|
added = 0
|
||||||
|
|
||||||
|
for f in existing_files:
|
||||||
|
search = File.query.filter_by(filename=f).first()
|
||||||
|
if search is None:
|
||||||
|
file = File(f, 0, default_category.id)
|
||||||
|
db.session.add(file)
|
||||||
|
added += 1
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
return added
|
||||||
|
|
||||||
|
|
||||||
|
@click.command('init-db')
|
||||||
|
@with_appcontext
|
||||||
|
def init_db_command():
|
||||||
|
"""
|
||||||
|
Creates the db file
|
||||||
|
"""
|
||||||
|
init_db()
|
||||||
|
|
||||||
|
|
||||||
|
@click.command("add-defaults")
|
||||||
|
@with_appcontext
|
||||||
|
def add_defaults_command():
|
||||||
|
"""
|
||||||
|
Initializes the database with default data
|
||||||
|
"""
|
||||||
|
add_defaults()
|
||||||
|
click.echo("Defaults added")
|
||||||
|
|
||||||
|
|
||||||
|
@click.command('generate-admin')
|
||||||
|
@with_appcontext
|
||||||
|
def generate_admin_command():
|
||||||
|
"""Creates the admin of the system"""
|
||||||
|
generate_admin()
|
||||||
|
click.echo('The admin was created')
|
||||||
|
|
||||||
|
|
||||||
|
@click.command('add-files')
|
||||||
|
@with_appcontext
|
||||||
|
def add_files_command():
|
||||||
|
"""
|
||||||
|
Generates the rows in the database for the files currently uploaded
|
||||||
|
They all will have default privacy, so, public
|
||||||
|
"""
|
||||||
|
added_size= add_files()
|
||||||
|
click.echo(f'added {added_size} files')
|
||||||
|
|
||||||
|
|
||||||
|
def init_app(app):
|
||||||
|
app.cli.add_command(init_db_command)
|
||||||
|
app.cli.add_command(add_defaults_command)
|
||||||
|
app.cli.add_command(generate_admin_command)
|
||||||
|
app.cli.add_command(add_files_command)
|
||||||
77
files/db.py
77
files/db.py
@@ -1,77 +0,0 @@
|
|||||||
import sqlite3
|
|
||||||
import click
|
|
||||||
|
|
||||||
from flask import current_app, g
|
|
||||||
from flask.cli import with_appcontext
|
|
||||||
from werkzeug.security import generate_password_hash
|
|
||||||
|
|
||||||
def get_db():
|
|
||||||
if 'db' not in g:
|
|
||||||
g.db = sqlite3.connect(
|
|
||||||
current_app.config['DATABASE'],
|
|
||||||
detect_types = sqlite3.PARSE_DECLTYPES
|
|
||||||
)
|
|
||||||
g.db.row_factory = sqlite3.Row
|
|
||||||
return g.db
|
|
||||||
|
|
||||||
def close_db(e=None):
|
|
||||||
db = g.pop('db', None)
|
|
||||||
|
|
||||||
if db is not None:
|
|
||||||
db.close()
|
|
||||||
|
|
||||||
def init_db():
|
|
||||||
db = get_db()
|
|
||||||
|
|
||||||
with current_app.open_resource('schema.sql') as f:
|
|
||||||
db.executescript(f.read().decode('utf8'))
|
|
||||||
|
|
||||||
def generate_admin():
|
|
||||||
db = get_db()
|
|
||||||
username = current_app.config['USERNAME']
|
|
||||||
password = current_app.config['PASSWORD']
|
|
||||||
|
|
||||||
db.execute(
|
|
||||||
'INSERT INTO users (username, password) VALUES (?, ?)',
|
|
||||||
(username, generate_password_hash(password),)
|
|
||||||
)
|
|
||||||
db.commit()
|
|
||||||
|
|
||||||
def generate_files():
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
@click.command('init-db')
|
|
||||||
@with_appcontext
|
|
||||||
def init_db_command():
|
|
||||||
"""Inits the database with it's schema"""
|
|
||||||
init_db()
|
|
||||||
click.echo('The database was initialized')
|
|
||||||
|
|
||||||
@click.command('generate-admin')
|
|
||||||
@with_appcontext
|
|
||||||
def generate_admin_command():
|
|
||||||
"""Creates the admin of the system"""
|
|
||||||
generate_admin()
|
|
||||||
click.echo('The admin was created')
|
|
||||||
|
|
||||||
|
|
||||||
@click.command('generate-files')
|
|
||||||
@with_appcontext
|
|
||||||
def generate_files_command():
|
|
||||||
"""
|
|
||||||
Generates the rows in the database for the files currently uploaded
|
|
||||||
They all will have default privacy, so, public
|
|
||||||
"""
|
|
||||||
generate_files()
|
|
||||||
click.echo('files generated')
|
|
||||||
|
|
||||||
|
|
||||||
def init_app(app):
|
|
||||||
app.teardown_appcontext(close_db)
|
|
||||||
app.cli.add_command(init_db_command)
|
|
||||||
app.cli.add_command(generate_admin_command)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
170
files/files.py
170
files/files.py
@@ -1,155 +1,74 @@
|
|||||||
import os
|
import os
|
||||||
import random
|
|
||||||
|
|
||||||
from flask import Flask, Blueprint, flash, request, redirect, url_for, current_app, render_template, send_from_directory, g
|
from flask import Flask, Blueprint, flash, request, redirect, url_for, current_app, render_template, \
|
||||||
|
send_from_directory, g
|
||||||
|
|
||||||
from werkzeug.utils import secure_filename
|
from werkzeug.utils import secure_filename
|
||||||
from werkzeug.exceptions import abort
|
from werkzeug.exceptions import abort
|
||||||
|
from files.models import db, File, Category, User
|
||||||
|
|
||||||
from files.auth import admin_required
|
from files.auth import admin_required
|
||||||
from files.db import get_db
|
|
||||||
|
|
||||||
|
|
||||||
bp = Blueprint('files', __name__)
|
bp = Blueprint('files', __name__)
|
||||||
bp.add_url_rule('/uploads/<path:filename>', 'uploaded_file', build_only=True)
|
bp.add_url_rule('/uploads/<path:filename>', 'uploaded_file', build_only=True)
|
||||||
|
|
||||||
def get_extension(filename):
|
|
||||||
return filename.rsplit('.', 1)[1].lower()
|
|
||||||
|
|
||||||
def get_path_in_upload(filename):
|
def _get_extension(filename):
|
||||||
|
return filename.rsplit('.', 1)[1].lower()
|
||||||
|
|
||||||
|
|
||||||
|
def _get_path_in_upload(filename):
|
||||||
return os.path.join(current_app.config['UPLOAD_FOLDER'], filename)
|
return os.path.join(current_app.config['UPLOAD_FOLDER'], filename)
|
||||||
|
|
||||||
def save_file(file, private, category):
|
|
||||||
|
def _save_file(file, private, category):
|
||||||
if private is None:
|
if private is None:
|
||||||
is_private = 0
|
is_private = 0
|
||||||
else:
|
else:
|
||||||
is_private = 1
|
is_private = 1
|
||||||
|
|
||||||
db = get_db()
|
|
||||||
|
|
||||||
filename = secure_filename(file.filename)
|
filename = secure_filename(file.filename)
|
||||||
db.execute(
|
|
||||||
'INSERT INTO files (filename, private, category)'
|
|
||||||
' VALUES (?,?,?)',
|
|
||||||
(filename, is_private, category['id'],)
|
|
||||||
)
|
|
||||||
|
|
||||||
file.save(get_path_in_upload(filename))
|
file.save(_get_path_in_upload(filename))
|
||||||
|
f = File(filename, is_private, category.id)
|
||||||
|
|
||||||
db.commit()
|
db.session.add(f)
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
return filename
|
|
||||||
|
|
||||||
def _rename_file(file, new_name):
|
def _rename_file(file, new_name):
|
||||||
db = get_db()
|
|
||||||
|
|
||||||
new_name = secure_filename(new_name)
|
new_name = secure_filename(new_name)
|
||||||
db.execute(
|
|
||||||
'UPDATE files'
|
|
||||||
' SET filename = ?'
|
|
||||||
' WHERE id = ?',
|
|
||||||
(new_name, file['id'],)
|
|
||||||
)
|
|
||||||
|
|
||||||
os.rename(get_path_in_upload(file['filename']), get_path_in_upload(new_name))
|
os.rename(_get_path_in_upload(file.filename), _get_path_in_upload(new_name))
|
||||||
|
file.filename = new_name
|
||||||
|
|
||||||
db.commit()
|
db.session.commit()
|
||||||
|
|
||||||
return new_name
|
|
||||||
|
|
||||||
|
|
||||||
def _delete_file(file):
|
def _delete_file(file):
|
||||||
db = get_db()
|
os.remove(_get_path_in_upload(file.filename))
|
||||||
db.execute(
|
db.session.delete(file)
|
||||||
'DELETE from files'
|
db.session.commit()
|
||||||
' WHERE id = ?',
|
|
||||||
(file['id'],)
|
|
||||||
)
|
|
||||||
|
|
||||||
os.remove(get_path_in_upload(file['filename']))
|
|
||||||
db.commit()
|
|
||||||
|
|
||||||
|
|
||||||
def get_files():
|
@bp.route('/', methods=['GET', 'POST'])
|
||||||
db = get_db()
|
|
||||||
files = db.execute(
|
|
||||||
'SELECT id, filename, private, category'
|
|
||||||
' FROM files'
|
|
||||||
' ORDER BY filename'
|
|
||||||
).fetchall()
|
|
||||||
return files
|
|
||||||
|
|
||||||
def get_public_files():
|
|
||||||
db = get_db()
|
|
||||||
files = db.execute(
|
|
||||||
'SELECT id, filename, private, category'
|
|
||||||
' FROM files'
|
|
||||||
' WHERE private = false'
|
|
||||||
' ORDER BY filename'
|
|
||||||
).fetchall()
|
|
||||||
|
|
||||||
return files
|
|
||||||
|
|
||||||
def get_files_by_category(id):
|
|
||||||
db = get_db()
|
|
||||||
files = db.execute(
|
|
||||||
'SELECT id, filename, private'
|
|
||||||
' FROM files'
|
|
||||||
' WHERE category = ?'
|
|
||||||
' ORDER BY filename',
|
|
||||||
(id,)
|
|
||||||
).fetchall()
|
|
||||||
|
|
||||||
def get_file(id):
|
|
||||||
db = get_db()
|
|
||||||
file = db.execute(
|
|
||||||
'SELECT id, filename, private, category'
|
|
||||||
' FROM files'
|
|
||||||
' WHERE id = ?'
|
|
||||||
' LIMIT 1',
|
|
||||||
(id,)
|
|
||||||
).fetchone()
|
|
||||||
return file
|
|
||||||
|
|
||||||
def get_categories():
|
|
||||||
db = get_db()
|
|
||||||
categories = db.execute(
|
|
||||||
'SELECT id, name'
|
|
||||||
' FROM categories'
|
|
||||||
' ORDER BY name'
|
|
||||||
).fetchall()
|
|
||||||
return categories
|
|
||||||
|
|
||||||
def get_category(id):
|
|
||||||
db = get_db()
|
|
||||||
category = db.execute(
|
|
||||||
'SELECT id, name'
|
|
||||||
' FROM categories'
|
|
||||||
' WHERE id = ?'
|
|
||||||
' LIMIT 1',
|
|
||||||
(id,)
|
|
||||||
).fetchone()
|
|
||||||
return category
|
|
||||||
|
|
||||||
@bp.route('/', methods=['GET', 'POST'])
|
|
||||||
def index():
|
def index():
|
||||||
if g.user is None:
|
if g.user is None:
|
||||||
files = get_public_files()
|
files = File.query.filter_by(private=0).all()
|
||||||
else:
|
else:
|
||||||
files = get_files()
|
files = File.query.all()
|
||||||
|
|
||||||
return render_template('files/index.html', files=files)
|
return render_template('files/index.html', files=files)
|
||||||
|
|
||||||
|
|
||||||
@bp.route('/upload', methods=['GET', 'POST'])
|
@bp.route('/upload', methods=['GET', 'POST'])
|
||||||
@admin_required
|
@admin_required
|
||||||
def upload_file():
|
def upload_file():
|
||||||
db = get_db()
|
|
||||||
|
|
||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
if 'file' not in request.files:
|
if 'file' not in request.files:
|
||||||
flash('No file part')
|
flash('No file part')
|
||||||
return redirect(request.url)
|
return redirect(request.url)
|
||||||
else:
|
else:
|
||||||
file = request.files['file']
|
file = request.files['file']
|
||||||
|
|
||||||
if 'private' not in request.form:
|
if 'private' not in request.form:
|
||||||
@@ -160,23 +79,27 @@ def upload_file():
|
|||||||
if 'category' not in request.form:
|
if 'category' not in request.form:
|
||||||
flash('No category selected')
|
flash('No category selected')
|
||||||
return redirect(request.url)
|
return redirect(request.url)
|
||||||
else:
|
|
||||||
category = get_category(request.form['category'])
|
category = Category.query.get(request.form['category'])
|
||||||
|
if category is None:
|
||||||
|
flash('The category selected won\'t exists')
|
||||||
|
return redirect(request.url)
|
||||||
|
|
||||||
if file.filename == '':
|
if file.filename == '':
|
||||||
flash('No seleted file')
|
flash('No seleted file')
|
||||||
return redirect(request.url)
|
return redirect(request.url)
|
||||||
|
|
||||||
if file:
|
if file:
|
||||||
filename = save_file(file, private, category)
|
_save_file(file, private, category)
|
||||||
return redirect(url_for('files.index'))
|
return redirect(url_for('files.index'))
|
||||||
|
|
||||||
return render_template('files/upload.html', categories=get_categories())
|
return render_template('files/upload.html', categories=Category.query.all())
|
||||||
|
|
||||||
|
|
||||||
@bp.route('/preview/<int:id>')
|
@bp.route('/preview/<int:id>')
|
||||||
def preview_file(id):
|
def preview_file(id):
|
||||||
file = get_file(id)
|
file = File.query.get(id)
|
||||||
if (file['private'] == 1 and g.user is not None) or (file['private'] == 0):
|
if (file.private == 1 and g.user is not None) or (file.private == 0):
|
||||||
return render_template('files/preview.html', file=file)
|
return render_template('files/preview.html', file=file)
|
||||||
else:
|
else:
|
||||||
return abort(404)
|
return abort(404)
|
||||||
@@ -186,28 +109,27 @@ def preview_file(id):
|
|||||||
@admin_required
|
@admin_required
|
||||||
def rename_file(id):
|
def rename_file(id):
|
||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
file = get_file(id)
|
file = File.query.get(id)
|
||||||
|
|
||||||
new_name = request.form['new_name'].lower()
|
new_name = request.form['new_name'].lower()
|
||||||
extension = file['filename'].rsplit('.', 1)[1].lower()
|
extension = file.filename.rsplit('.', 1)[1].lower()
|
||||||
|
|
||||||
if "." in new_name and get_extension(new_name):
|
if "." in new_name and _get_extension(new_name):
|
||||||
new_name = new_name.rsplit('.',1)[0] + '.' + extension
|
new_name = new_name.rsplit('.', 1)[0] + '.' + extension
|
||||||
else:
|
else:
|
||||||
new_name = new_name + '.' + extension
|
new_name = new_name + '.' + extension
|
||||||
|
|
||||||
_rename_file(file, new_name)
|
_rename_file(file, new_name)
|
||||||
|
|
||||||
return redirect(url_for('files.preview_file', id=file['id']))
|
return redirect(url_for('files.preview_file', id=file.id))
|
||||||
|
|
||||||
|
|
||||||
@bp.route('/delete/<int:id>', methods=['POST'])
|
@bp.route('/delete/<int:id>', methods=['POST'])
|
||||||
@admin_required
|
@admin_required
|
||||||
def delete_file(id):
|
def delete_file(id):
|
||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
file = get_file(id)
|
file = File.query.get(id)
|
||||||
print(file)
|
|
||||||
_delete_file(file)
|
_delete_file(file)
|
||||||
return redirect(url_for('index'))
|
return redirect(url_for('index'))
|
||||||
else:
|
else:
|
||||||
abort(404)
|
abort(404)
|
||||||
|
|
||||||
|
|||||||
64
files/models.py
Normal file
64
files/models.py
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
from flask_sqlalchemy import SQLAlchemy
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
db = SQLAlchemy()
|
||||||
|
|
||||||
|
|
||||||
|
class User(db.Model):
|
||||||
|
__tablename__ = 'users'
|
||||||
|
id = db.Column(db.Integer, primary_key=True)
|
||||||
|
username = db.Column(db.String, unique=True, nullable=False)
|
||||||
|
password = db.Column(db.String, nullable=False)
|
||||||
|
|
||||||
|
def __init__(self, username=None, password=None):
|
||||||
|
self.username = username
|
||||||
|
self.password = password
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return f'<User {self.username}>'
|
||||||
|
|
||||||
|
|
||||||
|
class Message(db.Model):
|
||||||
|
__tablename__ = 'messages'
|
||||||
|
id = db.Column(db.Integer, primary_key=True)
|
||||||
|
name = db.Column(db.String, nullable=False)
|
||||||
|
email = db.Column(db.String, nullable=False)
|
||||||
|
message = db.Column(db.Text, nullable=False)
|
||||||
|
sended = db.Column(db.DateTime, default=datetime.utcnow())
|
||||||
|
|
||||||
|
def __init__(self, name=None, email=None, message=None):
|
||||||
|
self.name = name
|
||||||
|
self.email = email
|
||||||
|
self.message = message
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return f'<Message from:{self.name} message:{self.message}>'
|
||||||
|
|
||||||
|
|
||||||
|
class File(db.Model):
|
||||||
|
__tablename__ = 'files'
|
||||||
|
id = db.Column(db.Integer, primary_key=True)
|
||||||
|
filename = db.Column(db.String, nullable=False)
|
||||||
|
private = db.Column(db.Integer, nullable=False)
|
||||||
|
category_id = db.Column(db.Integer, db.ForeignKey('categories.id'), nullable=False)
|
||||||
|
|
||||||
|
def __init__(self, filename=None, private=None, category_id=None):
|
||||||
|
self.filename = filename
|
||||||
|
self.private = private
|
||||||
|
self.category_id = category_id
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return f'<File name:{self.filename}>'
|
||||||
|
|
||||||
|
|
||||||
|
class Category(db.Model):
|
||||||
|
__tablename__ = 'categories'
|
||||||
|
id = db.Column(db.Integer, primary_key=True)
|
||||||
|
name = db.Column(db.String, nullable=False)
|
||||||
|
files = db.relationship('File', backref='category', lazy=True)
|
||||||
|
|
||||||
|
def __init__(self, name=None):
|
||||||
|
self.name = name
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return f'<Category name:{self.name}>'
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
DROP TABLE IF EXISTS users;
|
|
||||||
DROP TABLE IF EXISTS messages;
|
|
||||||
DROP TABLE IF EXISTS files;
|
|
||||||
DROP TABLE IF EXISTS categories;
|
|
||||||
|
|
||||||
CREATE TABLE users (
|
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
||||||
username TEXT NOT NULL,
|
|
||||||
password TEXT NOT NULL
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE messages (
|
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
||||||
name TEXT NOT NULL,
|
|
||||||
email TEXT NOT NULL,
|
|
||||||
message TEXT NOT NULL,
|
|
||||||
sended TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE files (
|
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
||||||
filename TEXT NOT NULL,
|
|
||||||
private INTEGER NOT NULL DEFAULT 0,
|
|
||||||
category INTEGER NOT NULL,
|
|
||||||
FOREIGN KEY(category) REFERENCES categories(id)
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE categories (
|
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
||||||
name TEXT NOT NULL
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
Reference in New Issue
Block a user