Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

update, make a favicon and make the storage_dir visable #1

Open
wants to merge 15 commits into
base: master
Choose a base branch
from
14 changes: 14 additions & 0 deletions .gitpod.Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
FROM gitpod/workspace-full

# Install Redis.
RUN sudo apt-get update \
&& sudo apt-get install -y \
redis-server mongodb \
&& sudo rm -rf /var/lib/apt/lists/*
EXPOSE 80
EXPOSE 443
# Install custom tools, runtimes, etc.
# For example "bastet", a command-line tetris clone:
# RUN brew install bastet
#
# More information: https://www.gitpod.io/docs/config-docker/
5 changes: 5 additions & 0 deletions .gitpod.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
image:
file: .gitpod.Dockerfile

tasks:
- init: pip install -r ./requirements.txt
4 changes: 3 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
flask
flask-session
redis
Pillow
pycrypto
asn1crypto==0.24.0
certifi==2018.4.16
cffi==1.11.5
dnspython
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

irrelevant dependency?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think so

chardet==3.0.4
ConfigArgParse==0.13.0
configobj==5.0.6
Expand Down
114 changes: 105 additions & 9 deletions server.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,9 @@
import datetime
import hashlib
import os
import secrets
import traceback
from os.path import splitext
import datetime import hashlib import os import secrets import traceback from os.path import splitext from
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Against PEP8

werkzeug.utils import secure_filename

import pymongo
from PIL import Image
from flask import Flask, request, json, jsonify, render_template, session, redirect
from flask import Flask, request, json, jsonify, render_template, session, redirect, send_from_directory
from flask_session import Session
from pymongo import MongoClient

Expand Down Expand Up @@ -86,6 +82,12 @@ def create_new_user(uid: int, name: str):
def datefromunix(s):
return datetime.datetime.fromtimestamp(int(s) / 1000).strftime("%Y-%m-%d %H:%M")

@app.route('/favicon.ico')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that should be done with nginx

def favicon():
return send_from_directory(os.path.join(app.root_path, 'static'),
'favicon.ico', mimetype='image/vnd.microsoft.icon')



@app.route('/')
def index():
Expand Down Expand Up @@ -121,7 +123,11 @@ def my_portal():
activity = _db.images.find({"user_uid": session.get("logged_in")}).sort('created', pymongo.DESCENDING).limit(5)
activity = [x for x in activity]
return render_template("my_portal.html", domains=allowed_domains, total=total, images=activity)

@app.route('/i/<filename>')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that should be done with nginx or storage provider

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you give an example?

def return_pic(filename):
""" Show just the image specified.
"""
return send_from_directory( _config['storage_dir'], secure_filename(filename))

@app.route('/my/files', methods=['GET', 'POST'])
def my_files():
Expand Down Expand Up @@ -193,6 +199,96 @@ def print_stats():
"files": _db.images.count(),
"domains": len(allowed_domains)
})
@app.route('/nl/')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should look into flask-babel

def index-nl():
return render_template("home.html", domains=allowed_domains)


@app.route('/nl/login', methods=['GET', 'POST'])
def login-nl():
if request.method == "POST":
usr = check_user(request.form.get("username", "unknown"), request.form.get("password", "unknown"))
if usr is None:
return render_template('login.html', is_error=True)
next_url = request.args.get("next", "/")
if session.get("logged_in"):
return redirect(next_url)
session['logged_in'] = usr.uid
return redirect(next_url)

else:
return render_template('nl/login.html', is_error=False)


@app.route('/nl/my/ip', methods=['GET'])
def my_ip-nl():
return jsonify({"ip": get_client_ip()})


@app.route('/nl/my', methods=['GET'])
def my_portal-nl():
if session.get("logged_in", None) is None:
return redirect("/login?next=/nl/my")
total = _db.images.find({"user_uid": session.get("logged_in")}).count()
activity = _db.images.find({"user_uid": session.get("logged_in")}).sort('created', pymongo.DESCENDING).limit(5)
activity = [x for x in activity]
return render_template("nl/my_portal.html", domains=allowed_domains, total=total, images=activity)


@app.route('/nl/my/files', methods=['GET', 'POST'])
def my_files-nl():
if session.get("logged_in", None) is None:
return redirect("/nl/login?next=/nl/my/files")

if request.method == "POST":
action = request.form.get("action")
if action == "delete_all":
total = _db.images.find({"user_uid": session.get("logged_in")})
for image in total:
fpath = "{}/{}{}".format(storage_folder, image['name'], image['extension'])
if os.path.exists(fpath):
os.remove(fpath)
else:
print("This file does not exist:", fpath)
_db.images.delete_many({"user_uid": session.get("logged_in")})
elif action == "delete_one":
name = request.form.get("name")
image = _db.images.find_one({"user_uid": session.get("logged_in"), "name": name})
if image:
fpath = "{}/{}{}".format(storage_folder, image['name'], image['extension'])
if os.path.exists(fpath):
os.remove(fpath)
else:
print("This file does not exist:", fpath)
return jsonify({"success": False})
_db.images.delete_one({"user_uid": session.get("logged_in"), "name": image['name']})
return jsonify({"success": True})

def get_page(page_size, page_num):
skips = page_size * (page_num - 1)
cursor = _db.images.find({"user_uid": session.get("logged_in")}) \
.skip(skips) \
.limit(page_size) \
.sort('created', pymongo.DESCENDING)
return [x for x in cursor]

page = int(request.args.get("page", 1))
images = get_page(50, page)
next_page = bool(get_page(50, page + 1))
next_page_num = page + 1
prev_page = page != 1
prev_page_num = page - 1

return render_template("nl/my_files.html", images=images, page=page, next_page=next_page, prev_page=prev_page,
next_page_num=next_page_num, prev_page_num=prev_page_num)


@app.route('/nl/my/config', methods=['GET'])
def my_config-nl():
if session.get("logged_in", None) is None:
return redirect("/nl/login?next=/nl/my/config")
usr = _db.users.find_one({"uid": session.get("logged_in")})
return render_template("nl/my_config.html", **{"domains": allowed_domains, "token": usr['token']})


@app.route('/upload', methods=['POST'])
Expand Down Expand Up @@ -234,7 +330,7 @@ def upload():

@app.route('/add-user', methods=['POST'])
def add_user():
token = request.headers.get("Authorization", "unknown")
token = request.form['Authorization']
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's an admin token, should be used in headers

if token != secret_key:
return "Not authorized", 403
if request.method == 'POST':
Expand Down
Binary file added static/favicon.ico
Binary file not shown.
64 changes: 64 additions & 0 deletions templates/nl/home.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
<!DOCTYPE html>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should look into flask-babel

<html lang="nl">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>ksoft.si :: Foto host</title>
<meta name="description"
content="Your personal image and file host. Fast and reliable CDN powered by nginx and CloudFlare, unlimited storage and multiple custom domains with SSL security. Brought to you by the Ksoft.Si team!">
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,300italic,700,700italic">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/normalize/3.0.3/normalize.css">
<link rel="stylesheet" href="https://cdn.ksoft.si/_generic/css/milligram.css">
<style>
body {
background: #FFFFFF url("https://cdn.ksoft.si/images/bg/camera3.jpg") center no-repeat fixed;
background-size: cover;
}

.container {
margin: 14vh auto;
position: relative;
padding-top: 2vh;
padding-bottom: 1vh;
background-color: rgba(255, 255, 255, 0.9);
}

.error {
color: red;
}

@media screen and (min-width: 620px) {
.container {
max-width: 500px;
width: 500px;
}
}
</style>
</head>
<body>
<div class="container">
<h2 style="text-align: center;margin-bottom: 0"> ksoft.si - Image host</h2>
<p style="text-align: center">Je persoonlijke geen bullshit foto host.</p>
<section>
<a class="button" href="/nl/my" style="width: 100%">Mijn account</a>
<a class="button" href="/nl/my/config" style="width: 100%">geef mijn config</a>
</section>
<section style="margin-top: 3rem">
<h4 style="text-align: center">List van domeinen:</h4>
<p style="text-align: center">
{% for d in domains %}
{{ d }}<br>
{% endfor %}
</p>
</section>
<small> Don't email us about account creation.| <a href="mailto:support.ksoft.si"> </a>.
</small>
</p>
<small> <a href="https://i.ksoft.si/"> english?
</a>
<br>
</small>
</section>
</div>
</body>
</html>
53 changes: 53 additions & 0 deletions templates/nl/login.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<!DOCTYPE html>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should look into flask-babel

<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Ksoft.Si :: Login</title>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,300italic,700,700italic">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/normalize/3.0.3/normalize.css">
<link rel="stylesheet" href="https://cdn.ksoft.si/_generic/css/milligram.css">
<style>
body {
background: #FFFFFF url("https://cdn.ksoft.si/images/bg/camera2.jpg") center no-repeat fixed;
background-size: cover;
}
.container {
margin: 14vh auto;
position: relative;
padding-top: 2vh;
padding-bottom: 1vh;
background-color: rgba(255,255,255,0.9);
}

.error {
color: red;
}

@media screen and (min-width: 620px) {
.container {
max-width: 500px;
width: 500px;
}
}
</style>
</head>
<body>
<div class="container">
<h2 style="text-align: center">Login</h2>
{% if is_error %}
<blockquote style="border-left: 0.3rem solid red;"><p class="error">Wrong username or password.</p></blockquote>
{% endif %}
<form method="post">
<fieldset>
<input type="text" name="username" id="username" placeholder="Username" required>
<input type="password" name="password" id="password" placeholder="Password" required>
<button type="submit" style="width: 100%">Login</button>
</fieldset>
</form>
<small>Don't email us about account creation. | <a href="mailto:[email protected]"> [email protected]</a>.</small>

</section>
</div>
</body>
</html>
96 changes: 96 additions & 0 deletions templates/nl/my_config.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
<!DOCTYPE html>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should look into flask-babel

<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Ksoft.Si :: Sharex Config</title>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,300italic,700,700italic">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/normalize/3.0.3/normalize.css">
<link rel="stylesheet" href="https://cdn.ksoft.si/_generic/css/milligram.css">
<style>
body {
background: #FFFFFF url("https://cdn.ksoft.si/images/bg/camera.jpg") center no-repeat fixed;
background-size: cover;
}

.container {
margin: 4vh auto;
position: relative;
padding-top: 3rem;
padding-bottom: 3rem;
background-color: rgba(255, 255, 255, 0.9);
}

.button-small {
font-size: .8rem;
height: 2.8rem;
line-height: 2.8rem;
padding: 0 1.5rem;
}

@media screen and (min-width: 1088px) {
.container {
max-width: 960px;
width: 960px;
}
}
</style>
</head>
<body>
<div class="container">
<h1 style="text-align: center">Jouw config:</h1>
<label for="domains">Select een url naar keuze:</label>
<select name="domains" id="domains">
{% for domain in domains %}
<option value="{{ domain }}">{{ domain }}</option>
{% endfor %}
</select>
<div class="clearfix">
<div class="float-left">
<small><a class="button button-outline button-small" href="/my">Back</a></small>
</div>
<div class="float-right">
<small><button class="copy button-small" data-clipboard-target="#config">Copy</button></small>
</div>
</div>
<pre><code id="config">{
"Name": "ksoft-sharex",
"DestinationType": "ImageUploader",
"RequestURL": "https://i.ksoft.si/upload",
"FileFormName": "image",
"Headers": {
"Authorization": "{{ token }}"
},
"URL": "https://i.ksoft.si/i/$json:filename$$json:extension$"
}</code></pre>
</div>
<script src="https://cdn.ksoft.si/_generic/jquery/jquery-3.2.1.min.js"></script>
<script src="https://cdn.ksoft.si/js/clipboard.min.js"></script>
<script>
let conf = {
"Name": "ksoft-sharex",
"DestinationType": "ImageUploader",
"RequestURL": "https://i.ksoft.si/upload",
"FileFormName": "image",
"Headers": {
"Authorization": "{{ token }}"
},
"URL": "https://i.ksoft.si/i/$json:filename$$json:extension$"
};

$('#domains').on('change', function () {
conf.URL = "https://" + $(this).val() + "/i/$json:filename$$json:extension$";
$("#config").text(JSON.stringify(conf, null, 2));
});
const copyToClipboard = str => {
const el = document.createElement('textarea');
el.value = str;
document.body.appendChild(el);
el.select();
document.execCommand('copy');
document.body.removeChild(el);
};
new ClipboardJS('.copy');
</script>
</body>
</html>
Loading