Commit c0538853 authored by Lukas Böhm's avatar Lukas Böhm 💬
Browse files

cleanup and add public Share View

parent 90bce98c
- Max Smietana (moralische untersützung)
- Lukas Böhm
- Jonas Pfister (Das Logo)
- Gabriel Denger (Hosting)
\ No newline at end of file
import os
from dotenv import load_dotenv
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from redis import Redis
from rq import Queue
basedir = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
datadir = os.path.abspath(os.path.join(basedir, 'data'))
load_dotenv(dotenv_path=os.path.join(basedir, '.env'), override=True)
app = Flask(__name__)
app.config['SECRET_KEY'] = os.getenv('SECRET_KEY') or 'jonas ist ein kek'
app.config['MEDIA_LOCATION'] = os.getenv('MEDIA_LOCATION') or os.path.join(datadir, 'media')
app.config['SQLALCHEMY_DATABASE_URI'] = os.getenv('DATABASE_URI') or 'sqlite:///' + os.path.join(datadir, 'home.db')
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.config['MAX_MEDIA_SIZE'] = 1000 * 1024 * 1024 # 1000 MB
app.config['REDIS_CONNECTION'] = os.getenv('REDIS_URL') or 'redis://'
app.config['MAX_CONTENT_LENGTH'] = 5 * 1024 * 1024 # 5 MB
app.config['REDIS_URI'] = os.getenv('REDIS_URL') or 'redis://'
app.jinja_env.globals['len'] = len
db = SQLAlchemy(app)
task_queue = Queue(connection=Redis.from_url(app.config['REDIS_CONNECTION']))
task_queue = Queue(connection=Redis.from_url(app.config['REDIS_URI']))
from app.models import *
db.create_all()
......
from wtforms import StringField, SubmitField, DateField, SelectField, FieldList, FormField, FileField, TextAreaField, \
MultipleFileField, SelectMultipleField, BooleanField, IntegerField
from wtforms.validators import DataRequired, Optional
MultipleFileField, SelectMultipleField, BooleanField, IntegerField, PasswordField
from wtforms.validators import DataRequired, Optional, length
from flask_wtf import FlaskForm
class UploadForm(FlaskForm):
files = MultipleFileField('Dateien hochladen', validators=[DataRequired()])
name = StringField(validators=[Optional()])
timer = SelectField(choices=[(0.25, '15 Sekunden'), (2, '2 Minuten'), (5, '5 Minuten'), (15, '15 Minuten'), (30, '30 Minuten'),
(120, '2 Stunden'), (360, '6 Stunden'), (1440, '1 Tag'), (10080, '7 Tage'),
(43200, '1 Monat'), (990000000, 'Für immer')],
label='Verfügbarkeit', validators=[DataRequired()], default=1440, coerce=float) # FIXME or int?
name = StringField(validators=[Optional(), length(max=64)])
timer = SelectField(choices=[(0.1, '6 Sekunden'), (2, '2 Minuten'), (5, '5 Minuten'), (15, '15 Minuten'),
(30, '30 Minuten'), (120, '2 Stunden'), (360, '6 Stunden'), (1440, '1 Tag'),
(10080, '7 Tage'), (43200, '1 Monat'), (-1, 'Für immer')],
label='Verfügbarkeit', validators=[DataRequired()], default=1440, coerce=float)
download_limit = SelectField(
choices=[(1, '1 Download'), (2, '2 Downloads'), (5, '5 Downloads'), (15, '15 Downloads'), (50, '50 Downloads'), (200, '200 Downloads'), (2147483646, 'Unbegrenzt')],
choices=[(1, '1 Download'), (2, '2 Downloads'), (5, '5 Downloads'), (15, '15 Downloads'), (50, '50 Downloads'), (200, '200 Downloads'), (-1, 'Unbegrenzt')],
label='Downloads', validators=[DataRequired()], default=5, coerce=int)
is_public = BooleanField('Öffentlich', default=False)
password = PasswordField('Passwort')
submit = SubmitField('Hochladen')
import os
from datetime import timedelta, datetime
from flask import current_app
from werkzeug.security import generate_password_hash
from app import db
......@@ -7,15 +11,18 @@ class Share(db.Model):
__tablename__ = 'Share'
id = db.Column('ID', db.String(64), primary_key=True, unique=True)
name = db.Column('Name', db.String(32), nullable=True)
expires = db.Column('Expires', db.DateTime, nullable=False)
name = db.Column('Name', db.String(64), nullable=True)
expires = db.Column('Expires', db.DateTime, nullable=True)
download_limit = db.Column('DownloadLimit', db.Integer, nullable=False)
is_public = db.Column('Public', db.Boolean, nullable=False, default=False)
password = db.Column('Password', db.String, nullable=True)
files = db.relationship('Attachment', backref='share')
def __repr__(self):
return f'<Share {self.id}>'
def __init__(self, name, expires, download_limit):
def __init__(self, name, timer, download_limit, is_public=False, password=None):
# create id
import uuid
share_id = str(uuid.uuid4())
......@@ -24,8 +31,10 @@ class Share(db.Model):
# set attributes
self.id = share_id
self.name = name
self.expires = expires
self.download_limit = download_limit
self.expires = datetime.now() + timedelta(minutes=float(timer)) if timer > 0 else None
self.download_limit = download_limit if download_limit > 0 else 100000000
self.is_public = is_public
self.password = generate_password_hash(password) if password else None
# create folder
try:
os.makedirs(os.path.join(current_app.config['MEDIA_LOCATION'], share_id))
......
......@@ -9,6 +9,18 @@ from app import task_queue as que
from shutil import make_archive, rmtree
def get_and_check_share(share_id):
share = Share.query.get(share_id)
if share is None:
return None
if share.download_limit <= 0:
que.enqueue(delete_share, share.id)
return None
return share
def delete_share(share_id):
with app.app_context():
share = Share.query.get_or_404(str(share_id))
......@@ -25,13 +37,6 @@ def delete_share(share_id):
db.session.commit()
@app.route('/delete/<string:share_id>')
def secret(share_id):
sh = Share.query.get_or_404(share_id)
job = que.enqueue_in(timedelta(seconds=8), delete_share, sh.id)
return str(job.id)
@app.route('/privacy')
def privacy():
return render_template('Privacy.html')
......@@ -57,6 +62,12 @@ def expired(e=None):
return render_template('Expired.html')
@app.route('/public')
def public():
shares = Share.query.filter_by(is_public=True).all()
return render_template('Public.html', shares=shares)
@app.route('/upload', methods=['GET', 'POST'])
def upload():
form = UploadForm()
......@@ -64,7 +75,7 @@ def upload():
if form.validate_on_submit():
# create share
share = Share(name=str(form.name.data),
expires=datetime.now() + timedelta(minutes=float(form.timer.data)),
timer=int(form.timer.data),
download_limit=int(form.download_limit.data)
)
db.session.add(share)
......@@ -79,7 +90,7 @@ def upload():
os.path.join(current_app.config['MEDIA_LOCATION'], share.id)
)
# put timer into scheduler queue
if form.timer.data != 990000000:
if share.expires:
que.enqueue_at(share.expires, delete_share, share.id)
# wrap it up
db.session.commit()
......@@ -88,16 +99,6 @@ def upload():
return render_template('Upload.html', form=form)
def get_and_check_share(share_id):
share = Share.query.get(share_id)
if share is None:
return None
if share.download_limit <= 0:
que.enqueue(delete_share, share.id)
return None
return share
@app.route('/shared/<string:share_id>')
def shared(share_id):
share = get_and_check_share(share_id)
......
......@@ -5,9 +5,13 @@
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="ChiefSend">
<meta name="author" content="Lukas Böhm, Jonas Pfister, Max Smietana (moralische Unterstützung)">
<link rel="icon" href="{{ url_for('static', filename='img/favicon.ico') }}">
<meta property="og:type" content="website">
<meta property="og:url" content="https://linkfork.co/">
<meta property="og:title" content="Chiefsend">
<meta property="og:description" content="Tolles ChiefSend ding.">
<title>{% block title %} ChiefSend {% endblock title %}</title>
<!-- Link CCS here -->
......
......@@ -5,6 +5,8 @@
<div class="d-flex flex-column flex-md-row align-items-center p-3 px-md-4 mb-3 bg-white border-bottom shadow-sm">
<h5 class="my-0 mr-md-auto font-weight-normal">ChiefSend</h5>
<nav class="my-2 my-md-0 mr-md-3">
<a class="my-0 font-weight-normal btn btn-primary" href="{{ url_for('upload') }}">Hochladen</a>
<a class="p-2 text-dark" href="{{ url_for('public') }}">Öffentlich</a>
<a class="p-2 text-dark" href="{{ url_for('privacy') }}">Privacy</a>
<a class="p-2 text-dark" href="{{ url_for('about') }}">About</a>
</nav>
......
{% extends 'BasePage.html' %}
{% block content %}
<div class="pricing-header px-3 py-3 pt-md-5 pb-md-4 mx-auto text-center">
<h1 class="display-4">Öffentliche Shares</h1>
<p class="lead">
Hier sehen Sie Shares die öffentlich hochgeladen wurden.
</p>
</div>
{# TODO live search bar
<div class="row">
<input type="text" class="form-control" placeholder="TODO TODO TODO TODO TODO"/>
</div> #}
{% for row in shares|batch(3, 'kek') %}
<div class="row">
{% for item in row %}
<div class="col md-4 my-4">
<div class="border rounded p-4 h-100">
<h3 class="mb-4 font-weight-normal">{{ item.name }}</h3>
<p class="lead">
ANZAHL Dateien (0 MB)
</p>
<p class="card-text">
<small class="text-muted">
Downloads verbleibend: {{ item.download_limit }},
Verfügbar bis: {{ item.expires }}
</small>
</p>
<a href="{{ url_for('download', share_id=item.id) }}">
<button class="btn btn-lg btn-primary" type="button">
Öffnen
</button>
</a>
</div>
</div>
{% endfor %}
</div>
{% endfor %}
{% endblock content %}
\ No newline at end of file
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment