Files
ai_platform/views/login.ejs

237 lines
7.3 KiB
Plaintext

<!doctype html>
<html lang="ko">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>서비스 접속 - XAVIS</title>
<link rel="stylesheet" href="/public/styles.css" />
<style>
.login-page {
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
padding: 24px 16px;
background: linear-gradient(160deg, #f0f4ff 0%, #f3f4f7 45%, #eef2f7 100%);
}
.login-card {
width: 100%;
max-width: 420px;
background: #fff;
border-radius: 16px;
box-shadow: 0 12px 40px rgba(15, 23, 42, 0.1);
padding: 32px 28px 28px;
border: 1px solid #e5e7eb;
}
.login-card .logo-img {
max-width: 200px;
margin: 0 auto 20px;
}
.login-card h1 {
margin: 0 0 8px;
font-size: 1.35rem;
color: #111827;
text-align: center;
}
.login-lead {
margin: 0 0 20px;
font-size: 14px;
color: #4b5563;
line-height: 1.6;
text-align: center;
}
.login-steps {
margin: 0 0 22px;
padding: 14px 16px;
background: #f8fafc;
border-radius: 10px;
font-size: 13px;
color: #374151;
line-height: 1.65;
}
.login-steps ol {
margin: 0;
padding-left: 1.25rem;
}
.login-steps li {
margin-bottom: 6px;
}
.login-steps li:last-child {
margin-bottom: 0;
}
.login-form label {
display: block;
font-size: 13px;
font-weight: 600;
color: #374151;
margin-bottom: 6px;
}
.login-form input[type="email"] {
width: 100%;
padding: 11px 12px;
border: 1px solid #d1d5db;
border-radius: 10px;
font-size: 15px;
}
.login-form input[type="email"]:focus {
outline: none;
border-color: #2563eb;
box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.15);
}
.login-form .btn-verify {
width: 100%;
margin-top: 16px;
padding: 12px 16px;
border: none;
border-radius: 10px;
background: #2563eb;
color: #fff;
font-size: 15px;
font-weight: 600;
cursor: pointer;
}
.login-form .btn-verify:hover {
background: #1d4ed8;
}
.login-form .btn-verify:disabled {
opacity: 0.65;
cursor: not-allowed;
}
.login-hint {
margin-top: 14px;
font-size: 12px;
color: #6b7280;
text-align: center;
}
.login-msg {
margin-top: 12px;
font-size: 13px;
text-align: center;
min-height: 1.2em;
}
.login-msg.ok {
color: #166534;
}
.login-msg.err {
color: #b91c1c;
}
</style>
</head>
<body>
<div class="login-page">
<div class="login-card">
<div class="login-logo-stack" style="display: flex; flex-direction: column; align-items: center; gap: 8px; margin-bottom: 12px">
<a
href="https://xavis.co.kr"
class="logo-link"
target="_blank"
rel="noopener noreferrer"
aria-label="XAVIS 회사 사이트(새 탭)"
style="margin-bottom: 0"
><img src="/public/images/xavis-logo.png" alt="XAVIS" class="logo-img logo-img-xavis"
/></a>
<img
src="/public/images/aiplatform-logo.png"
alt="AI PLATFORM"
class="logo-img"
style="cursor: default; margin-bottom: 0"
width="168"
height="auto"
decoding="async"
/>
</div>
<h1>서비스 접속</h1>
<p class="login-lead">회사 이메일로 본인 확인 후 서비스를 이용할 수 있습니다.</p>
<div class="login-steps">
<ol>
<li>아래에 <strong>@xavis.co.kr</strong> 이메일을 입력하고 <strong>검증</strong>을 누릅니다.</li>
<li>해당 메일함으로 전송된 <strong>인증 링크</strong>를 엽니다.</li>
<li>인증이 완료되면 바로 서비스 화면으로 이동합니다.</li>
</ol>
</div>
<form class="login-form" id="opsLoginForm" novalidate>
<input type="hidden" name="returnTo" id="returnTo" value="<%= typeof returnTo !== 'undefined' ? returnTo : '/learning' %>" />
<label for="opsEmail">회사 이메일</label>
<input
type="email"
id="opsEmail"
name="email"
required
autocomplete="email"
placeholder="name@xavis.co.kr"
inputmode="email"
/>
<button type="submit" class="btn-verify" id="opsVerifyBtn">검증</button>
<p class="login-msg" id="opsLoginMsg" role="status" aria-live="polite"></p>
<p class="login-hint">허용 도메인: @xavis.co.kr 만 가능합니다.</p>
</form>
</div>
</div>
<script>
(function () {
var form = document.getElementById("opsLoginForm");
var btn = document.getElementById("opsVerifyBtn");
var msg = document.getElementById("opsLoginMsg");
var emailEl = document.getElementById("opsEmail");
var returnToEl = document.getElementById("returnTo");
if (!form) return;
form.addEventListener("submit", function (e) {
e.preventDefault();
if (msg) {
msg.textContent = "";
msg.className = "login-msg";
}
var email = (emailEl && emailEl.value) ? emailEl.value.trim() : "";
if (!email) {
if (msg) {
msg.textContent = "이메일을 입력해 주세요.";
msg.className = "login-msg err";
}
return;
}
if (btn) btn.disabled = true;
fetch("/api/auth/request-link", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
email: email,
returnTo: (returnToEl && returnToEl.value) || "/learning",
}),
})
.then(function (r) {
return r.json().then(function (j) {
return { ok: r.ok, j: j };
});
})
.then(function (x) {
if (x.ok) {
if (msg) {
msg.textContent = x.j.message || "메일을 확인해 주세요.";
msg.className = "login-msg ok";
}
} else {
var err = (x.j && x.j.error) || "요청에 실패했습니다.";
if (msg) {
msg.textContent = err;
msg.className = "login-msg err";
}
if (err.indexOf("허용된 임직원") !== -1) {
alert("허용된 임직원이 아닙니다.");
}
}
})
.catch(function () {
if (msg) {
msg.textContent = "네트워크 오류가 발생했습니다.";
msg.className = "login-msg err";
}
})
.finally(function () {
if (btn) btn.disabled = false;
});
});
})();
</script>
</body>
</html>