feat(ui): /orderlist 미인증 시 dbx-main 로그인으로 위임, 프로필 헤더 개선

- 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 별도 커밋).
This commit is contained in:
2026-05-24 14:20:42 +09:00
parent 1eabd7183c
commit 931ad173ab
4 changed files with 163 additions and 51 deletions
+61 -11
View File
@@ -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 이 있으면 <img>, 없으면 폴백 아이콘
if user_picture:
avatar_html = (
f'<img src="{escape(user_picture, quote=True)}" '
f'alt="" class="profile-avatar-img" referrerpolicy="no-referrer" '
f'onerror="this.style.display=\'none\';'
f'this.nextElementSibling.style.display=\'flex\'">'
f'<i class="fa-solid fa-user profile-avatar-icon" style="display:none"></i>'
)
else:
avatar_html = '<i class="fa-solid fa-user profile-avatar-icon"></i>'
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="<h1>Welcome. static/index.html is missing.</h1>")