Compare commits

..

5 commits

Author SHA1 Message Date
Ada
103479668d add(backend): cache-control header
All checks were successful
ci/woodpecker/push/lint Pipeline was successful
Co-authored-by: Ada <ada@gnous.eu>
Co-committed-by: Ada <ada@gnous.eu>
2023-10-01 20:01:21 +00:00
Ada
e61eaa291b remove(lint): deprecated format option
All checks were successful
ci/woodpecker/push/lint Pipeline was successful
ci/woodpecker/pr/lint Pipeline was successful
2023-10-01 13:19:37 +02:00
Ada
6cee95c077 fix(backend: config): bad variable name for redis port 2023-10-01 13:19:36 +02:00
Ada
3448294314 add: readme 2023-10-01 13:19:21 +02:00
ed90e12227 update(deps): improve dev deps
All checks were successful
ci/woodpecker/push/lint Pipeline was successful
Add inheritance of requirements.txt to requirements.dev.txt
2023-09-28 20:52:22 +00:00
10 changed files with 59 additions and 134 deletions

11
README.md Normal file
View file

@ -0,0 +1,11 @@
# A code share paste
## Run
Require redis and python, install python requirements with `pip install -r requirements.txt`. Run with `flask --app paste run` (add `--debug` for devlopement)
You can configure with multiple environnement variables.
- PASTE_REDIS_HOST: redis host (default localhost)
- PASTE_REDIS_PORT: redis port (default 6379)
- PASTE_REDIS_USER: redis username (default none)
- PASTE_REDIS_PASSWORD: redis password (default none)
- PASTE_URL_LENGTH: length for url used by paste
- PASTE_SECRET_KEY: secret key for flask, don't use the default value (generate a safe value with `openssl rand -hex 16`).

View file

@ -2,7 +2,7 @@
from os import getenv from os import getenv
REDIS_HOST = getenv("PASTE_REDIS_HOST") or "localhost" REDIS_HOST = getenv("PASTE_REDIS_HOST") or "localhost"
REDIS_PORT = getenv("PASTE_REDIS_HOST") or "6379" REDIS_PORT = getenv("PASTE_REDIS_PORT") or "6379"
REDIS_USER = getenv("PASTE_REDIS_USER") or None REDIS_USER = getenv("PASTE_REDIS_USER") or None
REDIS_PASSWORD = getenv("PASTE_REDIS_PASSWORD") or None REDIS_PASSWORD = getenv("PASTE_REDIS_PASSWORD") or None
URL_LENGTH = getenv("PASTE_URL_LENGTH") or 4 URL_LENGTH = getenv("PASTE_URL_LENGTH") or 4

View file

@ -31,7 +31,7 @@ def insert_content_db(url_id: str, expiration: int, content: str, secret: str) -
:param secret: Secret key for delete url :param secret: Secret key for delete url
:param url_id: key for access to content. :param url_id: key for access to content.
:param expiration: Content expiration in second. :param expiration: Content expiration in second. Set 0 if no expiration.
:param content: Paste content :param content: Paste content
:return: None :return: None
""" """
@ -40,6 +40,7 @@ def insert_content_db(url_id: str, expiration: int, content: str, secret: str) -
data = {"content": content, "secret": secret} data = {"content": content, "secret": secret}
for key, key_content in data.items(): for key, key_content in data.items():
db.hset(url_id, key, key_content) db.hset(url_id, key, key_content)
db.expire(url_id, expiration) if expiration != 0:
db.expire(url_id, expiration)
else: else:
logging.error(f"Key : {url_id} already exist") # noqa: G004 logging.error(f"Key : {url_id} already exist") # noqa: G004

View file

@ -12,7 +12,7 @@ from flask import (
from werkzeug import Response from werkzeug import Response
from paste.db import check_content_exist, connect_redis, insert_content_db from paste.db import check_content_exist, connect_redis, insert_content_db
from paste.utils import generate_id, generate_secret from paste.utils import generate_id, generate_secret, get_expiration_time
home = Blueprint("home", __name__, url_prefix="/") home = Blueprint("home", __name__, url_prefix="/")
@ -49,11 +49,12 @@ def create() -> Response:
""" """
content = request.form.get("content") content = request.form.get("content")
time = request.form.get("time") time = request.form.get("time")
time = 86400 if time == "" else int(time) time = 0 if time == "" else int(time)
flash_data = create_paste(content, time) flash_data = create_paste(content, time)
flash(flash_data["url_id"], category="create") flash(flash_data["url_id"], category="create")
flash(flash_data["secret"], category="create") flash(flash_data["secret"], category="create")
return redirect(url_for("home.homepage")) return redirect(url_for("home.homepage"))
@ -74,7 +75,7 @@ def delete_paste(path: str) -> Response:
@home.route("/<path:path>") @home.route("/<path:path>")
def get_content(path: str) -> str: def get_content(path: str) -> Response | str:
""" """
Return paste for asked url. Return paste for asked url.
:param path: Requested path. :param path: Requested path.
@ -84,10 +85,13 @@ def get_content(path: str) -> str:
data = db.hgetall(path) data = db.hgetall(path)
if check_content_exist(path): if check_content_exist(path):
flash(data["content"]) flash(data["content"])
return render_template("content.html.j2") cache_time = get_expiration_time(path)
current_response = Response(render_template("content.html.j2"))
if cache_time == -1:
current_response.headers["Cache-Control"] = "public"
else:
current_response.headers["Cache-Control"] = f"public, {cache_time}"
return current_response
return abort(404) return abort(404)
@home.route("/about")
def about() -> str:
return render_template("about.html.j2")

View file

@ -1,65 +0,0 @@
:root {
--main-width: 85vw;
--bg-color: white;
--text-color: #212121;
--a-color: #7e22ce;
}
@media (prefers-color-scheme: dark) {
:root {
--bg-color: #212121;
--text-color: white;
}
}
/* large screen */
@media only screen and (min-width: 868px) {
:root {
--main-width: 50vw;
}
}
html {
font-family: sans-serif;
scroll-behavior: smooth;
color: var(--text-color);
}
body {
max-width: var(--main-width);
margin-left: auto;
margin-right: auto;
background-color: var(--bg-color);
line-height: 1.65
}
.menu ul {
list-style-type: none;
padding-left: 0;
}
.menu {
display: inline-block;
}
.menu li {
font-weight: bold;
}
a {
color: var(--a-color);
text-decoration: none;
}
a:hover {
color: black;
border-bottom: .2rem solid dimgrey;
}
textarea {
background-color: transparent;
resize: none;
width: 100%;
height: 80vh;
}

View file

@ -1,4 +0,0 @@
{% extends "base.html.j2" %}
{% block content %}
<p>Gnous/Paste</p>
{% endblock %}

View file

@ -1,22 +0,0 @@
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<title>Gnous/Paste</title>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<link rel="stylesheet" href="{{ url_for('static', filename='css/main.css') }}"/>
</head>
<body>
<nav class="menu">
<ul class="menu">
<li class="menu" {% if request.path == "/" %}><a href="{{ url_for("home.about") }}">About</a>{% elif request.path == "/about" %}<a href="{{ url_for("home.homepage") }}">Home</a>{% endif %}</li>
|
<li class="menu"><a href="https://git.gnous.eu/gnouseu/paste">Source</a></li>
</ul>
</nav>
<main>
{% block content %}
{% endblock %}
</main>
</body>
</html>

View file

@ -1,31 +1,21 @@
{% extends "base.html.j2" %} <form id="paste-content" method="POST" accept-charset="UTF-8" action="{{ url_for('home.create') }}">
{% block content %} <textarea name="content" form="paste-content">Enter text here...</textarea>
{% with messages = get_flashed_messages(category_filter=["create"]) %} <label for="time">Time in second</label>
<div class="infobox"> <input id="time" name="time">
{% if messages[0] %} <input type="submit">
<p>Paste : <a href="{{ request.base_url }}{{ messages[0] }}">{{ messages[0] }}</a> | <a </form>
href="{{ request.base_url }}delete/{{ messages[0] }}?secret={{ messages[1] }}">delete</a></p> {% with messages = get_flashed_messages(category_filter=["create"]) %}
{% endif %} {% if messages[0] %}
</div> <p>Paste available at : <a href="{{ request.base_url }}{{ messages[0] }}">{{ messages[0] }}</a></p>
{% endwith %} {% endif %}
{% if messages[1] %}
<p>You can delete paste with : <a href="{{ request.base_url }}delete/{{ messages[0] }}?secret={{ messages[1] }}">delete</a></p>
{% endif %}
{% endwith %}
{% with messages = get_flashed_messages(category_filter=["delete"]) %}
{% if messages[0] %}
<p>Paste {{ messages[0] }} deleted </p>
{% endif %}
{% endwith %}
{% with messages = get_flashed_messages(category_filter=["delete"]) %}
{% if messages[0] %}
<p>Paste {{ messages[0] }} deleted </p>
{% endif %}
{% endwith %}
<form class="container" id="paste-content" method="POST" accept-charset="UTF-8"
action="{{ url_for('home.create') }}">
<textarea name="content" form="paste-content" placeholder="Enter your text here"></textarea>
<input list="time" name="time">
<datalist id="time">
<option value="604800">7 days</option>
<option value="86400">1 day</option>
<option value="43200">12 hours</option>
<option value="21600">6 hours</option>
<option value="3600">1 hour</option>
<option value="600">10 minutes</option>
</datalist>
<input type="submit">
</form>
{% endblock %}

View file

@ -5,6 +5,7 @@ import secrets
import string import string
from paste import config from paste import config
from paste.db import connect_redis
def generate_id() -> str: def generate_id() -> str:
@ -24,3 +25,13 @@ def generate_secret() -> str:
:return: The secret. :return: The secret.
""" """
return secrets.token_hex() return secrets.token_hex()
def get_expiration_time(key: str) -> int:
"""
Get key expiration time
:param key: Key to check
:return: Expiration time in second.
"""
db = connect_redis()
return db.ttl(key)

View file

@ -1,3 +1,2 @@
flask==2.3.3 -r requirements.txt
redis==5.0.0
ruff==0.0.291 ruff==0.0.291