Compare commits
10 Commits
f1edf3218a
...
0525dec9b9
Author | SHA1 | Date | |
---|---|---|---|
0525dec9b9 | |||
4838bcfe59 | |||
e4f0105a6e | |||
b3feb1b7a0 | |||
e1ab344279 | |||
5fd50065cb | |||
05104667c7 | |||
98439fd1e7 | |||
4a1dd775eb | |||
528c474dc3 |
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,3 +1,2 @@
|
|||||||
__pycache__/
|
__pycache__/
|
||||||
|
|
||||||
*.py[cod]
|
*.py[cod]
|
||||||
|
5
backend/.dockerignore
Normal file
5
backend/.dockerignore
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
__pycache__/
|
||||||
|
*.pyc
|
||||||
|
README.md
|
||||||
|
forum.db
|
||||||
|
.gitignore
|
1
backend/.gitignore
vendored
Normal file
1
backend/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
forum.db
|
@ -1,4 +1,4 @@
|
|||||||
FROM python:bookworm
|
FROM python:alpine
|
||||||
|
|
||||||
EXPOSE 8000
|
EXPOSE 8000
|
||||||
|
|
||||||
@ -7,5 +7,5 @@ WORKDIR /app
|
|||||||
COPY requirements.txt .
|
COPY requirements.txt .
|
||||||
RUN pip install --no-cache-dir -r requirements.txt
|
RUN pip install --no-cache-dir -r requirements.txt
|
||||||
|
|
||||||
COPY forum.py .
|
COPY . .
|
||||||
CMD ["uvicorn", "forum:app", "--log-level", "debug", "--host", "0.0.0.0"]
|
CMD ["uvicorn", "main:app", "--log-level", "debug", "--host", "0.0.0.0"]
|
BIN
backend/forum.db
BIN
backend/forum.db
Binary file not shown.
131
backend/forum.py
131
backend/forum.py
@ -1,131 +0,0 @@
|
|||||||
from fastapi import FastAPI
|
|
||||||
import models
|
|
||||||
from database import engine, SessionLocal
|
|
||||||
from pydantic import BaseModel
|
|
||||||
|
|
||||||
app = FastAPI()
|
|
||||||
|
|
||||||
models.Base.metadata.create_all(bind=engine)
|
|
||||||
|
|
||||||
|
|
||||||
def get_db():
|
|
||||||
db = SessionLocal()
|
|
||||||
try:
|
|
||||||
yield db
|
|
||||||
finally:
|
|
||||||
db.close()
|
|
||||||
|
|
||||||
|
|
||||||
class Post:
|
|
||||||
"""Post is a single forum post or reply"""
|
|
||||||
id: int
|
|
||||||
thread_id: int
|
|
||||||
author: str
|
|
||||||
title: str
|
|
||||||
content: str
|
|
||||||
|
|
||||||
def __init__(self, id, thread_id, author, title, content):
|
|
||||||
self.id = id
|
|
||||||
self.thread_id = thread_id
|
|
||||||
self.author = author
|
|
||||||
self.title = title
|
|
||||||
self.content = content
|
|
||||||
|
|
||||||
|
|
||||||
class PostCreate(BaseModel):
|
|
||||||
"""Used when creating posts and replies"""
|
|
||||||
author: str = 'anon'
|
|
||||||
title: str
|
|
||||||
content: str
|
|
||||||
|
|
||||||
|
|
||||||
class Thread:
|
|
||||||
"""Thread is a collection of a post and its replies"""
|
|
||||||
id: int
|
|
||||||
author: str
|
|
||||||
title: str
|
|
||||||
closed: bool
|
|
||||||
|
|
||||||
def __init__(self, id, author, title, closed=False):
|
|
||||||
self.id = id
|
|
||||||
self.author = author
|
|
||||||
self.title = title
|
|
||||||
self.closed = closed
|
|
||||||
|
|
||||||
|
|
||||||
POSTS = []
|
|
||||||
THREADS = []
|
|
||||||
|
|
||||||
|
|
||||||
@app.get("/")
|
|
||||||
async def get_threads():
|
|
||||||
return THREADS
|
|
||||||
|
|
||||||
|
|
||||||
@app.post("/post")
|
|
||||||
async def create_post(data: PostCreate):
|
|
||||||
id = len(POSTS) + 1
|
|
||||||
author = data.author
|
|
||||||
title = data.title
|
|
||||||
content = data.content
|
|
||||||
|
|
||||||
# Create thread
|
|
||||||
thread = Thread(
|
|
||||||
id=id,
|
|
||||||
author=author,
|
|
||||||
title=title
|
|
||||||
)
|
|
||||||
THREADS.append(thread)
|
|
||||||
|
|
||||||
# Create post
|
|
||||||
post = Post(
|
|
||||||
id=id,
|
|
||||||
thread_id=id,
|
|
||||||
author=author,
|
|
||||||
title=title,
|
|
||||||
content=content
|
|
||||||
)
|
|
||||||
POSTS.append(post)
|
|
||||||
|
|
||||||
return post
|
|
||||||
|
|
||||||
|
|
||||||
@app.get("/{thread_id}")
|
|
||||||
async def get_thread(thread_id: int):
|
|
||||||
result = []
|
|
||||||
for post in POSTS:
|
|
||||||
if post.thread_id == thread_id:
|
|
||||||
result.append(post)
|
|
||||||
|
|
||||||
if len(result) == 0:
|
|
||||||
return {'error': 'could not find parent thread'}
|
|
||||||
|
|
||||||
return result
|
|
||||||
|
|
||||||
|
|
||||||
@app.post("/{thread_id}/post")
|
|
||||||
async def reply_to_post(thread_id: int, data: PostCreate):
|
|
||||||
parent = None
|
|
||||||
for thread in THREADS:
|
|
||||||
if thread.id == thread_id:
|
|
||||||
parent = thread
|
|
||||||
|
|
||||||
if parent is None:
|
|
||||||
return {'error': 'could not find parent thread'}
|
|
||||||
|
|
||||||
id = len(POSTS) + 1
|
|
||||||
author = data.author
|
|
||||||
title = data.title
|
|
||||||
content = data.content
|
|
||||||
|
|
||||||
# Create post
|
|
||||||
post = Post(
|
|
||||||
id=id,
|
|
||||||
thread_id=parent.id,
|
|
||||||
author=author,
|
|
||||||
title=title,
|
|
||||||
content=content
|
|
||||||
)
|
|
||||||
POSTS.append(post)
|
|
||||||
|
|
||||||
return post
|
|
108
backend/main.py
Normal file
108
backend/main.py
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
from typing import Annotated
|
||||||
|
from fastapi import FastAPI, Depends, HTTPException, Path
|
||||||
|
from sqlalchemy.orm import Session
|
||||||
|
from pydantic import BaseModel, Field
|
||||||
|
from starlette import status
|
||||||
|
|
||||||
|
from database import engine, SessionLocal
|
||||||
|
from models import Base, Post, Thread
|
||||||
|
|
||||||
|
app = FastAPI()
|
||||||
|
|
||||||
|
Base.metadata.create_all(bind=engine)
|
||||||
|
|
||||||
|
|
||||||
|
def get_db():
|
||||||
|
db = SessionLocal()
|
||||||
|
try:
|
||||||
|
yield db
|
||||||
|
finally:
|
||||||
|
db.close()
|
||||||
|
|
||||||
|
|
||||||
|
db_dependency = Annotated[Session, Depends(get_db)]
|
||||||
|
|
||||||
|
|
||||||
|
class PostCreate(BaseModel):
|
||||||
|
author: str = Field('anon')
|
||||||
|
title: str = Field('')
|
||||||
|
content: str = Field('')
|
||||||
|
|
||||||
|
|
||||||
|
@app.get('/', status_code=status.HTTP_200_OK)
|
||||||
|
async def get_posts(db: db_dependency):
|
||||||
|
return db.query(Post).all()
|
||||||
|
|
||||||
|
|
||||||
|
@app.post('/', status_code=status.HTTP_201_CREATED)
|
||||||
|
async def create_thread(db: db_dependency, data: PostCreate):
|
||||||
|
try:
|
||||||
|
# Create the post
|
||||||
|
post = Post(
|
||||||
|
author=data.author,
|
||||||
|
title=data.title,
|
||||||
|
content=data.content
|
||||||
|
)
|
||||||
|
db.add(post)
|
||||||
|
db.flush()
|
||||||
|
|
||||||
|
# Create the thread
|
||||||
|
thread = Thread(
|
||||||
|
author=post.author,
|
||||||
|
title=post.title,
|
||||||
|
content=post.content
|
||||||
|
)
|
||||||
|
db.add(thread)
|
||||||
|
db.flush()
|
||||||
|
|
||||||
|
# Update the Post with thread_id
|
||||||
|
post.thread_id = thread.id
|
||||||
|
db.commit()
|
||||||
|
|
||||||
|
return {
|
||||||
|
'id': post.id,
|
||||||
|
'author': post.author,
|
||||||
|
'title': post.title,
|
||||||
|
'content': post.content
|
||||||
|
}
|
||||||
|
except Exception as e:
|
||||||
|
db.rollback()
|
||||||
|
raise HTTPException(status_code=400, detail=str(e))
|
||||||
|
|
||||||
|
|
||||||
|
@app.get('/{thread_id}', status_code=status.HTTP_200_OK)
|
||||||
|
async def get_thread_by_id(db: db_dependency, thread_id: int = Path(gt=0)):
|
||||||
|
posts = db.query(Post).filter(Post.thread_id == thread_id).all()
|
||||||
|
if posts:
|
||||||
|
return posts
|
||||||
|
|
||||||
|
raise HTTPException(404, f'Could not find thread')
|
||||||
|
|
||||||
|
|
||||||
|
@app.post('/{thread_id}', status_code=status.HTTP_201_CREATED)
|
||||||
|
async def create_reply(db: db_dependency, data: PostCreate, thread_id: int = Path(gt=0)):
|
||||||
|
thread = db.query(Thread).filter(Thread.id == thread_id).first()
|
||||||
|
if thread:
|
||||||
|
post = Post(
|
||||||
|
thread_id=thread_id,
|
||||||
|
author=data.author,
|
||||||
|
title=data.title,
|
||||||
|
content=data.content
|
||||||
|
)
|
||||||
|
db.add(post)
|
||||||
|
db.commit()
|
||||||
|
|
||||||
|
return {
|
||||||
|
'id': post.id,
|
||||||
|
'author': post.author,
|
||||||
|
'title': post.title,
|
||||||
|
'content': post.content
|
||||||
|
}
|
||||||
|
|
||||||
|
raise HTTPException(status_code=404, detail='Could not find thread')
|
||||||
|
|
||||||
|
|
||||||
|
@app.get('/catalog', status_code=status.HTTP_200_OK)
|
||||||
|
async def get_catalog(db: db_dependency):
|
||||||
|
return db.query(Thread).all()
|
||||||
|
|
@ -1,8 +1,10 @@
|
|||||||
|
from sqlalchemy.orm import relationship
|
||||||
|
|
||||||
from database import Base
|
from database import Base
|
||||||
from sqlalchemy import Column, Integer, String, Boolean, ForeignKey
|
from sqlalchemy import Column, Integer, String, ForeignKey
|
||||||
|
|
||||||
|
|
||||||
class Posts(Base):
|
class Post(Base):
|
||||||
__tablename__ = 'posts'
|
__tablename__ = 'posts'
|
||||||
|
|
||||||
id = Column(Integer, primary_key=True)
|
id = Column(Integer, primary_key=True)
|
||||||
@ -12,11 +14,10 @@ class Posts(Base):
|
|||||||
content = Column(String)
|
content = Column(String)
|
||||||
|
|
||||||
|
|
||||||
class Threads(Base):
|
class Thread(Base):
|
||||||
__tablename__ = 'threads'
|
__tablename__ = 'threads'
|
||||||
|
|
||||||
id = Column(Integer, primary_key=True)
|
id = Column(Integer, primary_key=True)
|
||||||
author = Column(String)
|
author = Column(String)
|
||||||
title = Column(String)
|
title = Column(String)
|
||||||
content = Column(String)
|
content = Column(String)
|
||||||
is_closed = Column(Boolean, default=False)
|
|
Loading…
Reference in New Issue
Block a user