Skip to content

Commit 1447109

Browse files
Merge pull request #95 from socraticDevBlog/20250418-fastapibackend
feature: new fastapi backend
2 parents 99635dc + cb53711 commit 1447109

File tree

9 files changed

+202
-21
lines changed

9 files changed

+202
-21
lines changed

backend/Pipfile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ name = "pypi"
55

66
[packages]
77
fastapi = {extras = ["standard"], version = "*"}
8+
exceptiongroup = "*"
9+
tomli = "*"
810

911
[dev-packages]
1012
black = "*"

backend/Pipfile.lock

Lines changed: 67 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

backend/README.md

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,6 @@ avoid trouble by setting up these two environment variable before using `pipenv`
2020
# tell pipenv to use the /backend/Pipfile
2121
export PIPENV_PIPFILE=$(pwd)/Pipfile
2222
23-
# tell pipenv to create and use the .venv directory for virtual environment
24-
# in this directory here
25-
export PIPENV_VENV_IN_PROJECT=1
2623
```
2724

2825
## run api in dev mode
@@ -34,15 +31,25 @@ pipenv run fastapi dev
3431
## Docker
3532

3633
build the image locally
34+
3735
```
3836
docker build -t pastebin-backend .
3937
```
4038

4139
run the container
40+
4241
```
4342
docker run -p 8000:8000 pastebin-backend
4443
```
4544

4645
FastAPI app is available on: [http://localhost:8000](http://localhost:8000)
4746

47+
## API - locally
48+
49+
use built-in swagger
50+
51+
```
52+
pipenv run uvicorn app.main:app --reload
53+
```
4854

55+
<http://127.0.0.1:8000/docs>

backend/app/__init__.py

Whitespace-only changes.

backend/app/main.py

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,7 @@
1-
from typing import Union
2-
31
from fastapi import FastAPI
2+
from app.routes import router
43

54
app = FastAPI()
65

7-
8-
@app.get("/")
9-
def read_root():
10-
return {"Hello": "World"}
11-
12-
13-
@app.get("/items/{item_id}")
14-
def read_item(item_id: int, q: Union[str, None] = None):
15-
return {"item_id": item_id, "q": q}
6+
# Include the routes from the `routes.py` file
7+
app.include_router(router)

backend/app/mock_db.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
mock_db = {}

backend/app/models.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import time
2+
from typing import Optional
3+
4+
from pydantic import BaseModel
5+
from app.utils import hash_value
6+
7+
8+
class PasteModel(BaseModel):
9+
"""
10+
Paste model for creating and retrieving pastes
11+
with FastAPI.
12+
13+
Attributes:
14+
content (str): Content of the paste.
15+
workspace (Optional[str]): Optional workspace identifier.
16+
"""
17+
18+
content: str
19+
workspace: Optional[str]
20+
21+
22+
class PasteDataAware:
23+
"""
24+
Paste data model for creating and retrieving pastes
25+
with actual database
26+
27+
Attributes:
28+
id (str): Unique identifier for the paste.
29+
content (str): Content of the paste.
30+
client_id (str): Identifier for the client creating the paste.
31+
created_at (int): Timestamp of when the paste was created.
32+
"""
33+
34+
id: str
35+
content: str
36+
client_id: str
37+
created_at: int
38+
39+
def __init__(
40+
self,
41+
content: str,
42+
client_id: str,
43+
created_at: Optional[int] = None,
44+
id: str = None,
45+
):
46+
self.id = hash_value(value=content) if id is None else id
47+
self.content = content
48+
self.client_id = client_id
49+
self.created_at = int(time.time()) if created_at is None else created_at

backend/app/routes.py

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
from typing import Optional
2+
from fastapi import APIRouter, HTTPException, Request
3+
4+
from app.models import PasteModel, PasteDataAware
5+
from app.mock_db import mock_db
6+
7+
router = APIRouter()
8+
9+
10+
@router.get("/api/v1/pastes")
11+
async def get_pastes(client_id: Optional[str] = None, request: Request = None):
12+
"""
13+
GET /api/pastes
14+
Retrieve pastes for a specific client.
15+
"""
16+
if not client_id:
17+
client_id = request.client.host # Use client IP as fallback
18+
client_pastes = [
19+
paste for paste in mock_db.values() if paste.get("client_id") == client_id
20+
]
21+
return {"pastes": client_pastes}
22+
23+
24+
@router.get("/api/v1/{id}")
25+
async def get_paste(id: str, request: Request):
26+
"""
27+
GET /api/v1/{id}
28+
Retrieve a specific paste by ID.
29+
"""
30+
paste = mock_db.get(id)
31+
if not paste:
32+
raise HTTPException(status_code=404, detail="Paste not found")
33+
user_agent = request.headers.get("User-Agent", "")
34+
is_web_browser = "Mozilla" in user_agent or "AppleWebKit" in user_agent
35+
return {"paste": paste, "is_web_browser": is_web_browser}
36+
37+
38+
@router.post("/api/v1")
39+
async def create_paste(paste: PasteModel, request: Request):
40+
"""
41+
POST /api/v1
42+
Create a new paste.
43+
"""
44+
client_id = paste.workspace if paste.workspace != "" else request.client.host
45+
data = PasteDataAware(content=paste.content, client_id=client_id)
46+
mock_db[data.id] = {
47+
"id": data.id,
48+
"content": data.content,
49+
"client_id": data.client_id,
50+
"created_at": data.created_at,
51+
}
52+
return {"id": data.id}
53+
54+
55+
@router.options("/api/v1")
56+
async def options_api():
57+
"""
58+
OPTIONS /api
59+
Return allowed methods for the /api route.
60+
"""
61+
return {"methods": ["GET", "POST", "OPTIONS"]}

backend/app/utils.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import hashlib
2+
3+
4+
def hash_value(value: str, encoding: str = "utf-8") -> str:
5+
6+
value_bytes = str(value).encode(encoding=encoding)
7+
hash_object = hashlib.md5()
8+
hash_object.update(value_bytes)
9+
return hash_object.hexdigest()

0 commit comments

Comments
 (0)