first commit
This commit is contained in:
commit
cfd15eafd1
10 changed files with 201 additions and 0 deletions
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
.ruff_cache/
|
||||||
|
venv/
|
||||||
|
.idea/
|
0
paste/__init__.py
Normal file
0
paste/__init__.py
Normal file
13
paste/app.py
Normal file
13
paste/app.py
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
"""Main app file."""
|
||||||
|
|
||||||
|
from flask import Flask
|
||||||
|
|
||||||
|
from paste.home import home
|
||||||
|
|
||||||
|
app = Flask(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
app.register_blueprint(home)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
app.run()
|
8
paste/config.py
Normal file
8
paste/config.py
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
"""Manage app config from env."""
|
||||||
|
from os import getenv
|
||||||
|
|
||||||
|
REDIS_HOST = getenv("PASTE_REDIS_HOST") or "localhost"
|
||||||
|
REDIS_PORT = getenv("PASTE_REDIS_HOST") or "6379"
|
||||||
|
REDIS_USER = getenv("PASTE_REDIS_USER") or None
|
||||||
|
REDIS_PASSWORD = getenv("PASTE_REDIS_PASSWORD") or None
|
||||||
|
URL_LENGTH = getenv("PASTE_URL_LENGTH") or 4
|
40
paste/db.py
Normal file
40
paste/db.py
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
"""Manage db connection and insert."""
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from redis import Redis
|
||||||
|
|
||||||
|
from paste import config
|
||||||
|
|
||||||
|
|
||||||
|
def connect_redis() -> Redis:
|
||||||
|
"""
|
||||||
|
Connect to redis.
|
||||||
|
:return: Redis connection object.
|
||||||
|
"""
|
||||||
|
return Redis(host=config.REDIS_HOST, port=config.REDIS_PORT)
|
||||||
|
|
||||||
|
|
||||||
|
def check_content_exist(key: str) -> bool:
|
||||||
|
"""
|
||||||
|
Verify if key already exist in redis db.
|
||||||
|
:param key: Key to check.
|
||||||
|
:return: True if existed, else false.
|
||||||
|
"""
|
||||||
|
db = connect_redis()
|
||||||
|
return db.exists(key)
|
||||||
|
|
||||||
|
|
||||||
|
def insert_content_db(url_id: str, expiration: int, content: str) -> None:
|
||||||
|
"""
|
||||||
|
|
||||||
|
:param url_id: key for access to content.
|
||||||
|
:param expiration: Content expiration in second.
|
||||||
|
:param content: Paste content
|
||||||
|
:return: None
|
||||||
|
"""
|
||||||
|
if check_content_exist(url_id):
|
||||||
|
db = connect_redis()
|
||||||
|
db.set(url_id, content)
|
||||||
|
db.expire(url_id, expiration)
|
||||||
|
else:
|
||||||
|
logging.error(f"Key : {url_id} already exist")
|
51
paste/home.py
Normal file
51
paste/home.py
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
"""Manage view and create paste."""
|
||||||
|
from flask import Blueprint, redirect, render_template, request, url_for
|
||||||
|
|
||||||
|
from paste.db import check_content_exist, connect_redis, insert_content_db
|
||||||
|
from paste.utils import generate_id
|
||||||
|
|
||||||
|
home = Blueprint("home", __name__, url_prefix="/")
|
||||||
|
|
||||||
|
|
||||||
|
def create_paste(content: str) -> None:
|
||||||
|
"""
|
||||||
|
Create paste in DB.
|
||||||
|
:param content: Content to add in redis.
|
||||||
|
:return: None.
|
||||||
|
"""
|
||||||
|
url_id = generate_id()
|
||||||
|
while check_content_exist(url_id):
|
||||||
|
url_id = generate_id()
|
||||||
|
insert_content_db(url_id, 3600, content)
|
||||||
|
|
||||||
|
|
||||||
|
@home.route("/")
|
||||||
|
def homepage() -> str:
|
||||||
|
"""
|
||||||
|
Homepage.
|
||||||
|
:return: Homepage.
|
||||||
|
"""
|
||||||
|
return render_template("home.html.j2")
|
||||||
|
|
||||||
|
|
||||||
|
@home.route("/create", methods=["POST"])
|
||||||
|
def create() -> str:
|
||||||
|
"""
|
||||||
|
Receive POST data for create paste.
|
||||||
|
:return: Redirection to homapage.
|
||||||
|
"""
|
||||||
|
content = request.form.get("content")
|
||||||
|
create_paste(content)
|
||||||
|
return redirect(url_for("home.homepage"))
|
||||||
|
|
||||||
|
|
||||||
|
@home.route("/<path:path>")
|
||||||
|
def get_content(path: str) -> str:
|
||||||
|
"""
|
||||||
|
Return paste for asked url.
|
||||||
|
:param path: Requested path.
|
||||||
|
:return: Paste content.
|
||||||
|
"""
|
||||||
|
db = connect_redis()
|
||||||
|
print(db.get(path))
|
||||||
|
return "hello"
|
4
paste/templates/home.html.j2
Normal file
4
paste/templates/home.html.j2
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
<form id="paste-content" method="POST" accept-charset="UTF-8" action="{{ url_for('home.create') }}">
|
||||||
|
<input name="content">
|
||||||
|
<button>Create</button>
|
||||||
|
</form>
|
15
paste/utils.py
Normal file
15
paste/utils.py
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
"""Shared utils function."""
|
||||||
|
import random
|
||||||
|
import string
|
||||||
|
|
||||||
|
from paste import config
|
||||||
|
|
||||||
|
|
||||||
|
def generate_id() -> str:
|
||||||
|
"""
|
||||||
|
Generate url id format.
|
||||||
|
:return: Url id.
|
||||||
|
"""
|
||||||
|
return "".join(
|
||||||
|
random.choices(string.ascii_letters + string.digits, k=config.URL_LENGTH)
|
||||||
|
)
|
65
pyproject.toml
Normal file
65
pyproject.toml
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
[tool.ruff]
|
||||||
|
target-version = "py311"
|
||||||
|
line-length = 88
|
||||||
|
ignore = [
|
||||||
|
"D107", # Missing docstring in `__init__`
|
||||||
|
"D205", # 1 blank line required between summary line and description
|
||||||
|
"D401", # First line of docstring should be in imperative mood: "X"
|
||||||
|
"D203", # one-blank-line-before-class
|
||||||
|
"D212", # multi-line-summary-second-line
|
||||||
|
"PLR2004", # magic-value-comparison
|
||||||
|
]
|
||||||
|
select = [
|
||||||
|
"F", # Pyflakes
|
||||||
|
"E", # pycodestyle
|
||||||
|
"C90", # mccabe
|
||||||
|
"I", # isort
|
||||||
|
"N", # pep8-naming
|
||||||
|
"D", # pydocstyle
|
||||||
|
"UP", # pyupgrade
|
||||||
|
"ANN", # flake8-annotations
|
||||||
|
"S", # flake8-bandit
|
||||||
|
"BLE", # flake8-blind-except
|
||||||
|
"FBT", # flake8-boolean-trap
|
||||||
|
"B", # flake8-bugbear
|
||||||
|
"A", # flake8-builtins
|
||||||
|
"C4", # flake8-comprehensions
|
||||||
|
"EM", # flake8-errmsg
|
||||||
|
"ISC", # flake8-implicit-str-concat
|
||||||
|
"ICN", # flake8-import-conventions
|
||||||
|
"G", # flake8-logging-format
|
||||||
|
"INP", # flake8-no-pep420
|
||||||
|
"PIE", # flake8-pie
|
||||||
|
"T20", # flake8-print
|
||||||
|
"PYI", # flake8-pyi
|
||||||
|
"Q", # flake8-quotes
|
||||||
|
"RSE", # flake8-raise
|
||||||
|
"RET", # flake8-return
|
||||||
|
"SLOT", # flake8-slots
|
||||||
|
"SIM", # flake8-simplify
|
||||||
|
"TID", # flake8-tidy-imports
|
||||||
|
"TCH", # flake8-type-checking
|
||||||
|
"ARG", # flake8-unused-arguments
|
||||||
|
"PTH", # flake8-use-pathlib
|
||||||
|
"TD", # flake8-todos
|
||||||
|
"FIX", # flake8-fixme
|
||||||
|
"ERA", # eradicate
|
||||||
|
"PGH", # pygrep-hooks
|
||||||
|
"PL", # Pylint
|
||||||
|
"TRY", # tryceratops
|
||||||
|
"FLY", # flynt
|
||||||
|
"PERF", # Perflint
|
||||||
|
"RUF", # Ruff-specific rules
|
||||||
|
]
|
||||||
|
|
||||||
|
exclude = [
|
||||||
|
".git",
|
||||||
|
".ruff_cache",
|
||||||
|
"venv",
|
||||||
|
"instances",
|
||||||
|
"schema.sql"
|
||||||
|
]
|
||||||
|
format = "grouped"
|
||||||
|
|
||||||
|
[tool.isort]
|
||||||
|
profile = "black"
|
2
requirements.txt
Normal file
2
requirements.txt
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
flask==2.3.3
|
||||||
|
redis==5.0.0
|
Reference in a new issue