From 1a6e9b63270ddb56319663d23ad5c3e27bc196b3 Mon Sep 17 00:00:00 2001 From: qorgh529 Date: Fri, 10 Apr 2026 18:38:21 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20MY=20Page,=20=EA=B2=8C=EC=8B=9C?= =?UTF-8?q?=ED=8C=90,=20Favicon=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/main.py | 108 ++++++ frontend/index.html | 919 ++++++++++++++++++++++++++------------------ 2 files changed, 646 insertions(+), 381 deletions(-) diff --git a/backend/main.py b/backend/main.py index d2be17e..97160a6 100755 --- a/backend/main.py +++ b/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} diff --git a/frontend/index.html b/frontend/index.html index c4d5e53..bab8f5e 100755 --- a/frontend/index.html +++ b/frontend/index.html @@ -6,132 +6,165 @@ Web Portal - +
- +

🔒 비밀번호 변경 필요

@@ -154,7 +187,7 @@
- +
@@ -166,26 +199,28 @@
- +
-