feat: MY Page, 게시판, Favicon 기능 추가
Some checks failed
Build and Push Images / build-backend (push) Has been cancelled
Some checks failed
Build and Push Images / build-backend (push) Has been cancelled
This commit is contained in:
108
backend/main.py
108
backend/main.py
@@ -393,3 +393,111 @@ def update_user_pages(user_id: int, data: AccessUpdate, token=Depends(require_ad
|
||||
@app.get("/health")
|
||||
def health():
|
||||
return {"status": "ok"}
|
||||
|
||||
# ─── 게시판 ──────────────────────────────────────────────
|
||||
class PostCreate(BaseModel):
|
||||
title: str
|
||||
content: str
|
||||
|
||||
class ReplyCreate(BaseModel):
|
||||
content: str
|
||||
|
||||
def init_board_db():
|
||||
conn = psycopg2.connect(**DB_CONFIG)
|
||||
cur = conn.cursor()
|
||||
cur.execute("""
|
||||
CREATE TABLE IF NOT EXISTS board_posts (
|
||||
id SERIAL PRIMARY KEY,
|
||||
title VARCHAR(300) NOT NULL,
|
||||
content TEXT NOT NULL,
|
||||
author_id INTEGER REFERENCES users(id) ON DELETE SET NULL,
|
||||
author_name VARCHAR(100) NOT NULL,
|
||||
created_at TIMESTAMP DEFAULT NOW()
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS board_replies (
|
||||
id SERIAL PRIMARY KEY,
|
||||
post_id INTEGER REFERENCES board_posts(id) ON DELETE CASCADE,
|
||||
content TEXT NOT NULL,
|
||||
author_id INTEGER REFERENCES users(id) ON DELETE SET NULL,
|
||||
author_name VARCHAR(100) NOT NULL,
|
||||
created_at TIMESTAMP DEFAULT NOW()
|
||||
);
|
||||
""")
|
||||
conn.commit()
|
||||
cur.close()
|
||||
conn.close()
|
||||
|
||||
@app.on_event("startup")
|
||||
def startup_board():
|
||||
import time
|
||||
for _ in range(10):
|
||||
try:
|
||||
init_board_db()
|
||||
print("Board DB initialized")
|
||||
break
|
||||
except Exception as e:
|
||||
print(f"Board DB not ready... {e}")
|
||||
time.sleep(3)
|
||||
|
||||
@app.get("/api/board/posts")
|
||||
def list_posts(token=Depends(verify_token), conn=Depends(get_db)):
|
||||
cur = conn.cursor(cursor_factory=psycopg2.extras.RealDictCursor)
|
||||
cur.execute("SELECT id, title, author_name, created_at FROM board_posts ORDER BY created_at DESC")
|
||||
return cur.fetchall()
|
||||
|
||||
@app.post("/api/board/posts")
|
||||
def create_post(data: PostCreate, token=Depends(verify_token), conn=Depends(get_db)):
|
||||
cur = conn.cursor(cursor_factory=psycopg2.extras.RealDictCursor)
|
||||
cur.execute(
|
||||
"INSERT INTO board_posts (title, content, author_id, author_name) VALUES (%s, %s, %s, %s) RETURNING *",
|
||||
(data.title, data.content, int(token["sub"]), token["username"])
|
||||
)
|
||||
conn.commit()
|
||||
return cur.fetchone()
|
||||
|
||||
@app.get("/api/board/posts/{post_id}")
|
||||
def get_post(post_id: int, token=Depends(verify_token), conn=Depends(get_db)):
|
||||
cur = conn.cursor(cursor_factory=psycopg2.extras.RealDictCursor)
|
||||
cur.execute("SELECT * FROM board_posts WHERE id = %s", (post_id,))
|
||||
post = cur.fetchone()
|
||||
if not post:
|
||||
raise HTTPException(status_code=404, detail="Post not found")
|
||||
cur.execute("SELECT * FROM board_replies WHERE post_id = %s ORDER BY created_at ASC", (post_id,))
|
||||
replies = cur.fetchall()
|
||||
return {"post": post, "replies": replies}
|
||||
|
||||
@app.delete("/api/board/posts/{post_id}")
|
||||
def delete_post(post_id: int, token=Depends(verify_token), conn=Depends(get_db)):
|
||||
cur = conn.cursor(cursor_factory=psycopg2.extras.RealDictCursor)
|
||||
cur.execute("SELECT * FROM board_posts WHERE id = %s", (post_id,))
|
||||
post = cur.fetchone()
|
||||
if not post:
|
||||
raise HTTPException(status_code=404, detail="Post not found")
|
||||
if not token.get("is_admin") and post["author_id"] != int(token["sub"]):
|
||||
raise HTTPException(status_code=403, detail="Permission denied")
|
||||
cur.execute("DELETE FROM board_posts WHERE id = %s", (post_id,))
|
||||
conn.commit()
|
||||
return {"ok": True}
|
||||
|
||||
@app.post("/api/board/posts/{post_id}/replies")
|
||||
def create_reply(post_id: int, data: ReplyCreate, token=Depends(verify_token), conn=Depends(get_db)):
|
||||
cur = conn.cursor(cursor_factory=psycopg2.extras.RealDictCursor)
|
||||
cur.execute(
|
||||
"INSERT INTO board_replies (post_id, content, author_id, author_name) VALUES (%s, %s, %s, %s) RETURNING *",
|
||||
(post_id, data.content, int(token["sub"]), token["username"])
|
||||
)
|
||||
conn.commit()
|
||||
return cur.fetchone()
|
||||
|
||||
@app.delete("/api/board/replies/{reply_id}")
|
||||
def delete_reply(reply_id: int, token=Depends(verify_token), conn=Depends(get_db)):
|
||||
cur = conn.cursor(cursor_factory=psycopg2.extras.RealDictCursor)
|
||||
cur.execute("SELECT * FROM board_replies WHERE id = %s", (reply_id,))
|
||||
reply = cur.fetchone()
|
||||
if not reply:
|
||||
raise HTTPException(status_code=404, detail="Reply not found")
|
||||
if not token.get("is_admin") and reply["author_id"] != int(token["sub"]):
|
||||
raise HTTPException(status_code=403, detail="Permission denied")
|
||||
cur.execute("DELETE FROM board_replies WHERE id = %s", (reply_id,))
|
||||
conn.commit()
|
||||
return {"ok": True}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user