feat: 로그인 페이지 추가 (Basic Auth 제거)

- 산뜻한 로그인 페이지 (다크 테마 + 글로우 배경)
- 쿠키 기반 세션 인증 (24시간 유지)
- 로그아웃 버튼 (사이드바 + 상단바)
- 미인증 시 로그인 페이지로 리다이렉트

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
chpark
2026-03-27 01:57:38 +09:00
parent 6257025c8e
commit e1f7f1f2ad
4 changed files with 151 additions and 17 deletions
+7 -1
View File
@@ -101,12 +101,18 @@ tr:hover td{background:rgba(255,255,255,.02)}
<a href="/admin/adsense" class="<%= typeof page !== 'undefined' && page === 'adsense' ? 'active' : '' %>">&#x1f4b0; AdSense 관리</a>
<a href="/admin/domains" class="<%= typeof page !== 'undefined' && page === 'domains' ? 'active' : '' %>">&#x1f517; 도메인 매핑</a>
<a href="/admin/logs" class="<%= typeof page !== 'undefined' && page === 'logs' ? 'active' : '' %>">&#x1f4dd; 로그</a>
<div style="border-top:1px solid var(--border);margin-top:auto;padding-top:.5rem;margin-top:1rem">
<a href="/logout" style="color:var(--danger)">&#x1f6aa; 로그아웃</a>
</div>
</nav>
</aside>
<div class="main">
<div class="topbar">
<h1><%= typeof pageTitle !== 'undefined' ? pageTitle : '' %></h1>
<span class="text-muted" style="font-size:.8rem">Crawl Manager v1.0</span>
<div class="flex" style="gap:1rem">
<span class="text-muted" style="font-size:.8rem">Crawl Manager v1.0</span>
<a href="/logout" class="btn btn-outline btn-sm" style="font-size:.75rem">로그아웃</a>
</div>
</div>
<div class="content">
<%- body %>
+82
View File
@@ -0,0 +1,82 @@
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Crawl Manager - 로그인</title>
<style>
:root{--bg:#0f172a;--card:#1e293b;--border:#334155;--primary:#6366f1;--primary-hover:#818cf8;--text:#f1f5f9;--muted:#94a3b8;--danger:#ef4444;--radius:12px}
*{margin:0;padding:0;box-sizing:border-box}
body{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI','Noto Sans KR',sans-serif;background:var(--bg);color:var(--text);min-height:100vh;display:flex;align-items:center;justify-content:center;overflow:hidden}
/* 배경 애니메이션 */
.bg-glow{position:fixed;top:-50%;left:-50%;width:200%;height:200%;z-index:0}
.bg-glow::before,.bg-glow::after{content:'';position:absolute;border-radius:50%;filter:blur(80px);opacity:.15}
.bg-glow::before{top:20%;left:30%;width:400px;height:400px;background:#6366f1;animation:float 8s ease-in-out infinite}
.bg-glow::after{bottom:20%;right:30%;width:350px;height:350px;background:#06b6d4;animation:float 8s ease-in-out infinite reverse}
@keyframes float{0%,100%{transform:translate(0,0)}50%{transform:translate(30px,-30px)}}
.login-container{position:relative;z-index:1;width:100%;max-width:420px;padding:1rem}
.login-card{background:var(--card);border:1px solid var(--border);border-radius:16px;padding:2.5rem 2rem;box-shadow:0 25px 50px rgba(0,0,0,.3)}
.logo{text-align:center;margin-bottom:2rem}
.logo .icon{width:56px;height:56px;background:linear-gradient(135deg,#6366f1,#8b5cf6);border-radius:14px;display:inline-flex;align-items:center;justify-content:center;font-size:1.5rem;margin-bottom:1rem;box-shadow:0 8px 20px rgba(99,102,241,.3)}
.logo h1{font-size:1.3rem;font-weight:700;letter-spacing:-.5px}
.logo p{color:var(--muted);font-size:.82rem;margin-top:.3rem}
.form-group{margin-bottom:1.2rem}
.form-group label{display:block;font-size:.8rem;color:var(--muted);margin-bottom:.4rem;font-weight:500}
.form-group input{width:100%;padding:.75rem 1rem;background:var(--bg);border:1px solid var(--border);border-radius:var(--radius);color:var(--text);font-size:.92rem;transition:border-color .2s;outline:none}
.form-group input:focus{border-color:var(--primary);box-shadow:0 0 0 3px rgba(99,102,241,.15)}
.form-group input::placeholder{color:#475569}
.error-msg{background:rgba(239,68,68,.1);border:1px solid rgba(239,68,68,.25);border-radius:8px;padding:.6rem 1rem;margin-bottom:1.2rem;font-size:.82rem;color:var(--danger);display:flex;align-items:center;gap:.5rem}
.btn-login{width:100%;padding:.8rem;background:linear-gradient(135deg,#6366f1,#7c3aed);border:none;border-radius:var(--radius);color:#fff;font-size:.95rem;font-weight:600;cursor:pointer;transition:all .2s;margin-top:.5rem}
.btn-login:hover{background:linear-gradient(135deg,#818cf8,#8b5cf6);transform:translateY(-1px);box-shadow:0 8px 20px rgba(99,102,241,.3)}
.btn-login:active{transform:translateY(0)}
.footer-text{text-align:center;margin-top:1.5rem;font-size:.75rem;color:#475569}
</style>
</head>
<body>
<div class="bg-glow"></div>
<div class="login-container">
<div class="login-card">
<div class="logo">
<div class="icon">CM</div>
<h1>Crawl Manager</h1>
<p>관리자 로그인</p>
</div>
<% if (error) { %>
<div class="error-msg">
<span>&#x26a0;</span> <%= error %>
</div>
<% } %>
<form method="POST" action="/login">
<input type="hidden" name="redirect" value="<%= redirect %>">
<div class="form-group">
<label>이메일</label>
<input type="email" name="email" placeholder="admin@example.com" required autofocus>
</div>
<div class="form-group">
<label>비밀번호</label>
<input type="password" name="password" placeholder="비밀번호를 입력하세요" required>
</div>
<button type="submit" class="btn-login">로그인</button>
</form>
<div class="footer-text">Crawl Manager v1.0</div>
</div>
</div>
</body>
</html>