Skip to content

Commit

Permalink
Merge pull request #37 from Mr-Sunglasses/release/2
Browse files Browse the repository at this point in the history
Release/2
  • Loading branch information
Mr-Sunglasses authored Dec 30, 2023
2 parents 2b69f39 + f8d4d01 commit e690027
Show file tree
Hide file tree
Showing 8 changed files with 122 additions and 42 deletions.
22 changes: 22 additions & 0 deletions .github/workflows/mypy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
name: mypy paste.py 🐍

on:
pull_request:
branches: [main]

jobs:
build:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v3
- name: Setup PDM
uses: pdm-project/setup-pdm@v3
with:
python-version: 3.11
cache: true
cache-dependency-path: "**/pdm.lock"
- name: Install dependencies
run: pdm install
- name: Run mypy
run: pdm mypy
3 changes: 3 additions & 0 deletions mypy.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[mypy]
ignore_missing_imports = True
disallow_untyped_defs = True
34 changes: 32 additions & 2 deletions pdm.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ license = {text = "MIT"}
[tool.pdm.scripts]
start = "uvicorn src.paste.main:app --host 0.0.0.0 --port 8080 --workers 4"
test = "pytest"
mypy = "mypy src/paste"

[tool.pdm.dev-dependencies]
test = [
Expand All @@ -33,3 +34,10 @@ lint = [
hooks = [
"pre-commit>=3.6.0",
]
typing = [
"mypy>=1.8.0",
]

[tool.ruff]
line-length = 160
exclude = ["data/*", ".git"]
62 changes: 37 additions & 25 deletions src/paste/main.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,24 @@
from fastapi import File, UploadFile, HTTPException, status, Request, Form, FastAPI, Header
from fastapi.responses import PlainTextResponse, HTMLResponse, RedirectResponse, JSONResponse
from fastapi import (
File,
UploadFile,
HTTPException,
status,
Request,
Form,
FastAPI,
Header,
Response,
)
from fastapi.responses import (
PlainTextResponse,
HTMLResponse,
RedirectResponse,
JSONResponse,
)
import shutil
import os
import json
from pathlib import Path
from fastapi import FastAPI
from fastapi.templating import Jinja2Templates
from fastapi.middleware.cors import CORSMiddleware
from slowapi.errors import RateLimitExceeded
Expand All @@ -16,7 +30,7 @@
from pygments.lexers import get_lexer_by_name, guess_lexer
from pygments.formatters import HtmlFormatter
from pygments.util import ClassNotFound
from typing import List, Optional, Any
from typing import List, Optional

limiter = Limiter(key_func=get_remote_address)
app: FastAPI = FastAPI(title="paste.py 🐍")
Expand All @@ -41,29 +55,31 @@

BASE_DIR: Path = Path(__file__).resolve().parent

templates: Jinja2Templates = Jinja2Templates(
directory=str(Path(BASE_DIR, "templates")))
templates: Jinja2Templates = Jinja2Templates(directory=str(Path(BASE_DIR, "templates")))


@app.post("/file")
@limiter.limit("100/minute")
async def post_as_a_file(request: Request, file: UploadFile = File(...)) -> PlainTextResponse:
async def post_as_a_file(
request: Request, file: UploadFile = File(...)
) -> PlainTextResponse:
try:
uuid: str = generate_uuid()
if uuid in large_uuid_storage:
uuid = generate_uuid()
# Extract file extension from the filename
try:
file_extension: str = Path(file.filename).suffix[1:]
path: str = f"data/{uuid}.{file_extension}"
file_extension: Optional[str] = None
if file.filename is not None:
file_extension = Path(file.filename).suffix[1:]
path: str = f"data/{uuid}{file_extension}"
except Exception:
path = f"data/{uuid}"
finally:
val: str = "/".join(path.split("/")[1:])
with open(path, "wb") as f:
shutil.copyfileobj(file.file, f)
large_uuid_storage.append(uuid)
print(large_uuid_storage)
except Exception:
raise HTTPException(
detail="There was an error uploading the file",
Expand All @@ -75,7 +91,9 @@ async def post_as_a_file(request: Request, file: UploadFile = File(...)) -> Plai


@app.get("/paste/{uuid}")
async def get_paste_data(uuid: str, user_agent: Optional[str] = Header(None)) -> Any:
async def get_paste_data(
uuid: str, user_agent: Optional[str] = Header(None)
) -> Response:
path: str = f"data/{uuid}"
try:
with open(path, "rb") as f:
Expand All @@ -97,13 +115,11 @@ async def get_paste_data(uuid: str, user_agent: Optional[str] = Header(None)) ->
try:
lexer = get_lexer_by_name(file_extension, stripall=True)
except ClassNotFound:
lexer = get_lexer_by_name(
"text", stripall=True) # Default lexer
lexer = get_lexer_by_name("text", stripall=True) # Default lexer
formatter = HtmlFormatter(
style="colorful", full=True, linenos="inline", cssclass='code')
style="colorful", full=True, linenos="inline", cssclass="code"
)
highlighted_code: str = highlight(content, lexer, formatter)

print(highlighted_code)
custom_style = """
.code pre span.linenos {
color: #999;
Expand Down Expand Up @@ -154,19 +170,16 @@ async def get_paste_data(uuid: str, user_agent: Optional[str] = Header(None)) ->
</body>
</html>
"""
return HTMLResponse(
content=response_content
)
except Exception as e:
print(e)
return HTMLResponse(content=response_content)
except Exception:
raise HTTPException(
detail="404: The Requested Resource is not found",
status_code=status.HTTP_404_NOT_FOUND,
)


@app.get("/", response_class=HTMLResponse)
async def indexpage(request: Request) -> HTMLResponse:
async def indexpage(request: Request) -> Response:
return templates.TemplateResponse("index.html", {"request": request})


Expand All @@ -187,7 +200,7 @@ async def delete_paste(uuid: str) -> PlainTextResponse:


@app.get("/web", response_class=HTMLResponse)
async def web(request: Request) -> HTMLResponse:
async def web(request: Request) -> Response:
return templates.TemplateResponse("web.html", {"request": request})


Expand All @@ -209,8 +222,7 @@ async def web_post(
with open(path, "wb") as f:
f.write(file_content)
large_uuid_storage.append(uuid_)
except Exception as e:
print(e)
except Exception:
raise HTTPException(
detail="There was an error uploading the file",
status_code=status.HTTP_403_FORBIDDEN,
Expand Down
23 changes: 13 additions & 10 deletions src/paste/templates/index.html
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>paste.py 🐍</title>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<meta name="og:title" content="paste.py 🐍"/>
<meta name="og:site_name" content="paste.py"/>
<meta name="og:description" content="A simple pastebin powered by FastAPI."/>
<meta name="og:type" content="website"/>
<meta name="og:url" content="https://paste.fosscu.org"/>
<meta name="og:locale" content="en_US"/>
<title>paste.py 🐍</title>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="og:title" content="paste.py 🐍" />
<meta name="og:site_name" content="paste.py" />
<meta
name="og:description"
content="A simple pastebin powered by FastAPI."
/>
<meta name="og:type" content="website" />
<meta name="og:url" content="https://paste.fosscu.org" />
<meta name="og:locale" content="en_US" />
<style>
body {
font-family: sans-serif;
Expand Down Expand Up @@ -109,7 +112,7 @@ <h3>EXAMPLES</h3>
A shell function that can be added to <strong>.bashrc</strong> or <strong>.bash_profle</strong> or <strong>.zshrc</strong> for
quick pasting from the command line. The command takes a filename or reads
from stdin if none was supplied and outputs the URL of the paste to
stdout: <pre>paste file.txt</pre> or <pre>echo "hi" | paste</pre></p>
stdout: <pre>paste file.txt</pre> <!-- or <pre>echo "hi" | paste</pre></p> -->


</pre>
Expand Down
9 changes: 5 additions & 4 deletions src/paste/utils.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
import random
import string
import os
from pathlib import Path


def generate_uuid():
def generate_uuid() -> str:
# Combine uppercase letters, lowercase letters, and digits
characters = string.ascii_letters + string.digits
characters: str = string.ascii_letters + string.digits

# Generate a random 4-character code
random_code = ''.join(random.choice(characters) for _ in range(4))
random_code: str = "".join(random.choice(characters) for _ in range(4))

return random_code


def extract_extension(file_name):
def extract_extension(file_name: Path) -> str:
_, extension = os.path.splitext(file_name)
return extension
3 changes: 2 additions & 1 deletion tests/test_api.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
from fastapi.testclient import TestClient
from src.paste.main import app
from typing import Optional
import os

client: TestClient = TestClient(app)

file: str = None
file: Optional[str] = None


def test_get_health_route() -> None:
Expand Down

0 comments on commit e690027

Please sign in to comment.