🏠 홈 카드 관리
cyanburu.com 메인 허브 홈페이지에 표시될 카드를 관리합니다.
| 순서 | +제목 | +부제목 | +URL | +태그 | +공개 | +관리 | +
|---|
diff --git a/backend/main.py b/backend/main.py
index 8af671e..f506166 100755
--- a/backend/main.py
+++ b/backend/main.py
@@ -802,3 +802,136 @@ def delete_notice_reply(reply_id: int, token=Depends(verify_token), conn=Depends
cur.execute("DELETE FROM notice_replies WHERE id = %s", (reply_id,))
conn.commit()
return {"ok": True}
+
+# ─── Homepage Cards ──────────────────────────────────────────────────────────
+# 이 코드를 기존 main.py 맨 아래에 붙여넣으세요.
+# init_db() 함수 안의 cur.execute("""...""") 블록에도 아래 테이블을 추가해야 합니다.
+# (init_db 수정 방법은 아래 주석 참고)
+#
+# [init_db() 수정] 기존 CREATE TABLE ... 블록 마지막에 추가:
+#
+# CREATE TABLE IF NOT EXISTS homepage_cards (
+# id SERIAL PRIMARY KEY,
+# title VARCHAR(100) NOT NULL,
+# subtitle VARCHAR(200),
+# description TEXT,
+# url VARCHAR(500) NOT NULL,
+# tag VARCHAR(20) DEFAULT 'LIVE',
+# sort_order INTEGER DEFAULT 0,
+# visible BOOLEAN DEFAULT TRUE,
+# created_at TIMESTAMP DEFAULT NOW()
+# );
+#
+# ─────────────────────────────────────────────────────────────────────────────
+
+def init_homepage_cards_db():
+ conn = psycopg2.connect(**DB_CONFIG)
+ cur = conn.cursor()
+ cur.execute("""
+ CREATE TABLE IF NOT EXISTS homepage_cards (
+ id SERIAL PRIMARY KEY,
+ title VARCHAR(100) NOT NULL,
+ subtitle VARCHAR(200),
+ description TEXT,
+ url VARCHAR(500) NOT NULL,
+ tag VARCHAR(20) DEFAULT 'LIVE',
+ sort_order INTEGER DEFAULT 0,
+ visible BOOLEAN DEFAULT TRUE,
+ created_at TIMESTAMP DEFAULT NOW()
+ );
+ """)
+ # 기본 카드 초기 데이터 (최초 1회만)
+ cur.execute("SELECT COUNT(*) FROM homepage_cards")
+ count = cur.fetchone()[0]
+ if count == 0:
+ default_cards = [
+ ("Web Portal", "내부 서비스 관리", "로그인 후 할당된 웹 서비스 목록을 확인하고 접속할 수 있는 통합 포털.", "/portal", "LIVE", 1),
+ ("King's Cup", "멀티플레이 술게임", "디스코드 화면공유로 친구들과 함께 즐기는 킹컵 카드 게임. 실시간 WebSocket 기반.", "/kingscup", "NEW", 2),
+ ("Gitea", "셀프호스티드 Git", "GitHub 대신 자체 서버에서 운영하는 Git 저장소. Container Registry 포함.", "https://gitea.cyanburu.com", "LIVE", 3),
+ ("ArgoCD", "GitOps 배포 엔진", "Gitea 저장소를 감시해 K8s 클러스터에 자동으로 배포하는 GitOps 도구.", "https://argo.cyanburu.com", "LIVE", 4),
+ ]
+ cur.executemany(
+ "INSERT INTO homepage_cards (title, subtitle, description, url, tag, sort_order) VALUES (%s,%s,%s,%s,%s,%s)",
+ default_cards
+ )
+ conn.commit()
+ cur.close()
+ conn.close()
+
+@app.on_event("startup")
+def startup_homepage_cards():
+ import time
+ for _ in range(10):
+ try:
+ init_homepage_cards_db()
+ print("Homepage cards DB initialized")
+ break
+ except Exception as e:
+ print(f"Homepage cards DB not ready... {e}")
+ time.sleep(3)
+
+# ── Public API (인증 불필요 - 허브 홈페이지에서 호출) ──────────────────────
+@app.get("/api/homepage/cards")
+def get_homepage_cards(conn=Depends(get_db)):
+ """허브 홈페이지(cyanburu.com/)에서 카드 목록을 가져옵니다. 인증 불필요."""
+ cur = conn.cursor(cursor_factory=psycopg2.extras.RealDictCursor)
+ cur.execute("""
+ SELECT id, title, subtitle, description, url, tag, sort_order
+ FROM homepage_cards
+ WHERE visible = TRUE
+ ORDER BY sort_order ASC, id ASC
+ """)
+ return cur.fetchall()
+
+# ── Admin API (관리자 전용) ────────────────────────────────────────────────
+class HomepageCardCreate(BaseModel):
+ title: str
+ subtitle: Optional[str] = ""
+ description: Optional[str] = ""
+ url: str
+ tag: Optional[str] = "LIVE"
+ sort_order: Optional[int] = 0
+ visible: Optional[bool] = True
+
+@app.get("/api/admin/homepage-cards")
+def list_homepage_cards(token=Depends(require_admin), conn=Depends(get_db)):
+ """관리자: 전체 카드 목록 (비공개 포함)"""
+ cur = conn.cursor(cursor_factory=psycopg2.extras.RealDictCursor)
+ cur.execute("SELECT * FROM homepage_cards ORDER BY sort_order ASC, id ASC")
+ return cur.fetchall()
+
+@app.post("/api/admin/homepage-cards")
+def create_homepage_card(data: HomepageCardCreate, token=Depends(require_admin), conn=Depends(get_db)):
+ """관리자: 카드 추가"""
+ cur = conn.cursor(cursor_factory=psycopg2.extras.RealDictCursor)
+ cur.execute(
+ """INSERT INTO homepage_cards (title, subtitle, description, url, tag, sort_order, visible)
+ VALUES (%s, %s, %s, %s, %s, %s, %s) RETURNING *""",
+ (data.title, data.subtitle, data.description, data.url, data.tag, data.sort_order, data.visible)
+ )
+ conn.commit()
+ return cur.fetchone()
+
+@app.put("/api/admin/homepage-cards/{card_id}")
+def update_homepage_card(card_id: int, data: HomepageCardCreate, token=Depends(require_admin), conn=Depends(get_db)):
+ """관리자: 카드 수정"""
+ cur = conn.cursor(cursor_factory=psycopg2.extras.RealDictCursor)
+ cur.execute(
+ """UPDATE homepage_cards
+ SET title=%s, subtitle=%s, description=%s, url=%s, tag=%s, sort_order=%s, visible=%s
+ WHERE id=%s RETURNING *""",
+ (data.title, data.subtitle, data.description, data.url, data.tag, data.sort_order, data.visible, card_id)
+ )
+ result = cur.fetchone()
+ if not result:
+ raise HTTPException(status_code=404, detail="Card not found")
+ conn.commit()
+ return result
+
+@app.delete("/api/admin/homepage-cards/{card_id}")
+def delete_homepage_card(card_id: int, token=Depends(require_admin), conn=Depends(get_db)):
+ """관리자: 카드 삭제"""
+ cur = conn.cursor()
+ cur.execute("DELETE FROM homepage_cards WHERE id=%s", (card_id,))
+ conn.commit()
+ return {"ok": True}
\ No newline at end of file
diff --git a/frontend/index.html b/frontend/index.html
index 449a103..6fe5a1f 100755
--- a/frontend/index.html
+++ b/frontend/index.html
@@ -210,6 +210,7 @@
+
cyanburu.com 메인 허브 홈페이지에 표시될 카드를 관리합니다.🏠 홈 카드 관리
+
+
+
+
+
+
+ 순서
+ 제목
+ 부제목
+ URL
+ 태그
+ 공개
+ 관리
+