From 931ad173abe0b249f8401b37bc01d908df6eae84 Mon Sep 17 00:00:00 2001 From: king Date: Sun, 24 May 2026 14:20:42 +0900 Subject: [PATCH] =?UTF-8?q?feat(ui):=20/orderlist=20=EB=AF=B8=EC=9D=B8?= =?UTF-8?q?=EC=A6=9D=20=EC=8B=9C=20dbx-main=20=EB=A1=9C=EA=B7=B8=EC=9D=B8?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=EC=9C=84=EC=9E=84,=20=ED=94=84=EB=A1=9C?= =?UTF-8?q?=ED=95=84=20=ED=97=A4=EB=8D=94=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - read_root() 가 세션 미인증 시 dbx-main /login?next=/orderlist/ 로 302. OMS 는 더 이상 자체 로그인 UI 를 가지지 않는다. - index.html: #login-screen 블록 제거, dashboard 만 렌더. - 우측 상단 프로필 영역을 메인 페이지 스타일로 개편: 이름 + 이메일 + 아바타(Google picture URL, 폴백 아이콘) + 로그아웃 텍스트 버튼. - main.py 가 세션의 user_name/user_email/user_picture 를 HTML placeholder 로 치환. - app.js checkAuth() 단순화: 화면 토글 제거, 401 시 dbx-main 로그인으로 자동 이동. - style.css 에 새 프로필 영역 CSS 추가. Google 아바타가 보이려면 dbx-main 도 같이 user_picture 를 세션 top-level 에 저장하도록 수정됨 (dbx-main 별도 커밋). --- app/main.py | 72 +++++++++++++++++++++++++++++++++------- static/css/style.css | 78 ++++++++++++++++++++++++++++++++++++++++++++ static/index.html | 40 ++++++++--------------- static/js/app.js | 24 ++++++-------- 4 files changed, 163 insertions(+), 51 deletions(-) diff --git a/app/main.py b/app/main.py index c9bafe3..50bbe1a 100644 --- a/app/main.py +++ b/app/main.py @@ -1,7 +1,9 @@ from fastapi import FastAPI, Depends, Request from fastapi.staticfiles import StaticFiles -from fastapi.responses import HTMLResponse, JSONResponse +from fastapi.responses import HTMLResponse, JSONResponse, RedirectResponse from starlette.middleware.sessions import SessionMiddleware +from html import escape +from urllib.parse import quote import logging import os @@ -68,18 +70,66 @@ app.include_router(settings.router, prefix="/api/settings", tags=["settings"]) os.makedirs("static", exist_ok=True) app.mount("/static", StaticFiles(directory="static"), name="static") +MAIN_LOGIN_URL = os.getenv("MAIN_LOGIN_URL", "https://dbx.no1king.freeddns.org/login") + + @app.get("/", response_class=HTMLResponse) -async def read_root(): - # Return the main index.html file +async def read_root(request: Request): + # 미인증 사용자는 dbx-main 의 로그인 페이지로 위임 (SSO 원칙). + # OMS 는 자체 로그인 UI 를 보여주지 않는다. + user_email = request.session.get("user_email") + if not user_email: + next_path = f"{APP_ROOT_PATH}/" if APP_ROOT_PATH else "/" + sep = "&" if "?" in MAIN_LOGIN_URL else "?" + target = f"{MAIN_LOGIN_URL}{sep}next={quote(next_path, safe='/')}" + return RedirectResponse(url=target, status_code=302) + + user_name = request.session.get("user_name") or user_email + user_picture = request.session.get("user_picture", "") or "" + + # 아바타: Google picture URL 이 있으면 , 없으면 폴백 아이콘 + if user_picture: + avatar_html = ( + f'' + f'' + ) + else: + avatar_html = '' + if os.path.exists("static/index.html"): with open("static/index.html", "r", encoding="utf-8") as f: content = f.read() - content = content.replace("__APP_BASE_PATH__", APP_ROOT_PATH) - content = content.replace('src="/static/', f'src="{APP_ROOT_PATH}/static/' if APP_ROOT_PATH else 'src="/static/') - content = content.replace('href="/static/', f'href="{APP_ROOT_PATH}/static/' if APP_ROOT_PATH else 'href="/static/') - content = content.replace("window.location.href='/login", f"window.location.href='{APP_ROOT_PATH}/login") - content = content.replace('window.location.href="/login', f'window.location.href="{APP_ROOT_PATH}/login') - content = content.replace("window.location.href='/logout", f"window.location.href='{APP_ROOT_PATH}/logout") - content = content.replace('window.location.href="/logout', f'window.location.href="{APP_ROOT_PATH}/logout') - return HTMLResponse(content=content) + content = content.replace("__APP_BASE_PATH__", APP_ROOT_PATH) + content = content.replace( + 'src="/static/', + f'src="{APP_ROOT_PATH}/static/' if APP_ROOT_PATH else 'src="/static/', + ) + content = content.replace( + 'href="/static/', + f'href="{APP_ROOT_PATH}/static/' if APP_ROOT_PATH else 'href="/static/', + ) + content = content.replace( + "window.location.href='/login", + f"window.location.href='{APP_ROOT_PATH}/login", + ) + content = content.replace( + 'window.location.href="/login', + f'window.location.href="{APP_ROOT_PATH}/login', + ) + content = content.replace( + "window.location.href='/logout", + f"window.location.href='{APP_ROOT_PATH}/logout", + ) + content = content.replace( + 'window.location.href="/logout', + f'window.location.href="{APP_ROOT_PATH}/logout', + ) + # 사용자 정보 주입 (HTML 안의 placeholder 치환) + content = content.replace("__USER_NAME__", escape(user_name)) + content = content.replace("__USER_EMAIL__", escape(user_email)) + content = content.replace("__USER_AVATAR__", avatar_html) + return HTMLResponse(content=content) return HTMLResponse(content="

Welcome. static/index.html is missing.

") diff --git a/static/css/style.css b/static/css/style.css index ba1347a..a8df4b6 100644 --- a/static/css/style.css +++ b/static/css/style.css @@ -1769,4 +1769,82 @@ input[type="checkbox"] { } } +/* ── 우측 상단 프로필 영역 (메인 페이지 스타일) ───────────── */ +.nav-profile-area .profile-info { + display: flex; + align-items: center; + gap: 10px; +} + +.nav-profile-area .profile-name-block { + display: flex; + flex-direction: column; + line-height: 1.2; + text-align: right; +} + +.nav-profile-area .profile-name { + font-size: 14px; + font-weight: 700; + color: inherit; + white-space: nowrap; +} + +.nav-profile-area .profile-email { + font-size: 11px; + color: rgba(120, 120, 130, 0.75); + white-space: nowrap; +} + +.nav-profile-area .profile-avatar { + width: 36px; + height: 36px; + border-radius: 50%; + overflow: hidden; + display: flex; + align-items: center; + justify-content: center; + background: rgba(0, 0, 0, 0.08); + flex-shrink: 0; +} + +.nav-profile-area .profile-avatar-img { + width: 100%; + height: 100%; + object-fit: cover; + display: block; +} + +.nav-profile-area .profile-avatar-icon { + color: rgba(80, 80, 90, 0.7); + font-size: 16px; +} + +.nav-profile-area .btn-logout-text { + display: inline-flex; + align-items: center; + gap: 6px; + padding: 6px 12px; + background: rgba(0, 0, 0, 0.05); + border: 1px solid rgba(0, 0, 0, 0.1); + border-radius: 8px; + color: inherit; + font-size: 12px; + font-weight: 600; + cursor: pointer; + transition: background 0.15s, border-color 0.15s; + line-height: 1; +} + +.nav-profile-area .btn-logout-text:hover { + background: rgba(0, 0, 0, 0.1); + border-color: rgba(0, 0, 0, 0.2); +} + +/* 모바일에서 이메일은 숨겨 공간 절약 */ +@media (max-width: 720px) { + .nav-profile-area .profile-email { display: none; } + .nav-profile-area .btn-logout-text span { display: none; } +} + /* Force UI Update */ diff --git a/static/index.html b/static/index.html index 0805591..6477eb2 100644 --- a/static/index.html +++ b/static/index.html @@ -20,26 +20,9 @@
- -
- -
- - - - +