diff --git a/cloud/public/assets/login.js b/cloud/public/assets/login.js new file mode 100644 index 0000000..464f6cc --- /dev/null +++ b/cloud/public/assets/login.js @@ -0,0 +1,75 @@ +/** + * Login form handler: intercepts form submission and submits via fetch. + * On success (200), redirects to dashboard. On error (401/423), shows + * the #login-error element with a user-friendly message. + * + * XSS-safe: all dynamic values are set via textContent, never innerHTML. + */ +(function () { + 'use strict'; + + var form = document.querySelector('.login-form'); + if (!form) return; + + var errorEl = document.getElementById('login-error'); + + form.addEventListener('submit', function (e) { + e.preventDefault(); + + // Hide any previous error message + if (errorEl) { + errorEl.setAttribute('hidden', ''); + errorEl.textContent = ''; + } + + // Collect form data and convert to application/x-www-form-urlencoded + var formData = new FormData(form); + var body = new URLSearchParams(formData).toString(); + + fetch('/login', { + method: 'POST', + headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, + body: body + }) + .then(function (res) { + if (res.status === 200) { + // Success: redirect to dashboard + window.location.href = '/'; + return null; + } + // Error: parse response and show error message + if (!res.ok) throw new Error('login_failed:' + res.status); + return res.json(); + }) + .then(function (data) { + if (data && data.error) { + showError(data.error); + } + }) + .catch(function (err) { + // Extract status code from error message if available + var status = /login_failed:(\d+)/.exec(String(err)) ? parseInt(RegExp.$1) : 0; + if (status === 401 || status === 0) { + showError('invalid_credentials'); + } else if (status === 423) { + showError('locked'); + } + }); + }); + + function showError(errorCode) { + if (!errorEl) return; + + var message; + if (errorCode === 'invalid_credentials') { + message = '로그인 실패'; + } else if (errorCode === 'locked') { + message = '계정 잠김'; + } else { + message = '로그인 실패'; + } + + errorEl.textContent = message; + errorEl.removeAttribute('hidden'); + } +})(); diff --git a/cloud/src/View/login.php b/cloud/src/View/login.php index b1adb22..0991000 100644 --- a/cloud/src/View/login.php +++ b/cloud/src/View/login.php @@ -20,5 +20,6 @@ render_header('로그인');
+