login.html 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. <!DOCTYPE html>
  2. <html lang="zh-CN">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  6. <title>Login - Q2API</title>
  7. <style>
  8. :root {
  9. --bg:#0a0e1a;
  10. --panel:#0f1420;
  11. --muted:#8b95a8;
  12. --text:#e8f0ff;
  13. --accent:#4f8fff;
  14. --danger:#ff4757;
  15. --border:#1a2332;
  16. --glow:rgba(79,143,255,.15);
  17. }
  18. * { box-sizing:border-box; }
  19. html, body { height:100%; margin:0; }
  20. body {
  21. background:radial-gradient(ellipse at top, #0f1624 0%, #0a0e1a 100%);
  22. color:var(--text);
  23. font-family:ui-sans-serif,system-ui,-apple-system,Segoe UI,Roboto,PingFang SC,Hiragino Sans GB,Microsoft YaHei,Noto Sans,Arial,sans-serif;
  24. line-height:1.6;
  25. display:flex;
  26. align-items:center;
  27. justify-content:center;
  28. min-height:100vh;
  29. padding:20px;
  30. }
  31. .container {
  32. width:100%;
  33. max-width:420px;
  34. }
  35. .header {
  36. text-align:center;
  37. margin-bottom:32px;
  38. }
  39. h1 {
  40. font-size:32px;
  41. font-weight:700;
  42. margin:0 0 8px;
  43. background:linear-gradient(135deg,#4f8fff,#7b9fff);
  44. -webkit-background-clip:text;
  45. -webkit-text-fill-color:transparent;
  46. background-clip:text;
  47. }
  48. .subtitle {
  49. color:var(--muted);
  50. font-size:14px;
  51. }
  52. .panel {
  53. background:linear-gradient(145deg,rgba(15,20,32,.8),rgba(10,14,26,.9));
  54. border:1px solid var(--border);
  55. border-radius:16px;
  56. padding:32px;
  57. box-shadow:0 20px 60px rgba(0,0,0,.4),0 0 0 1px rgba(79,143,255,.08),inset 0 1px 0 rgba(255,255,255,.03);
  58. backdrop-filter:blur(12px);
  59. }
  60. .field {
  61. margin-bottom:24px;
  62. }
  63. label {
  64. display:block;
  65. color:var(--muted);
  66. font-size:13px;
  67. font-weight:500;
  68. margin-bottom:8px;
  69. }
  70. input {
  71. width:100%;
  72. background:rgba(12,16,28,.6);
  73. color:var(--text);
  74. border:1px solid var(--border);
  75. border-radius:12px;
  76. padding:12px 14px;
  77. outline:none;
  78. transition:all .2s;
  79. font-size:14px;
  80. box-shadow:inset 0 1px 2px rgba(0,0,0,.2);
  81. }
  82. input:focus {
  83. border-color:var(--accent);
  84. box-shadow:0 0 0 3px var(--glow),inset 0 1px 2px rgba(0,0,0,.2);
  85. background:rgba(12,16,28,.8);
  86. }
  87. button {
  88. width:100%;
  89. background:linear-gradient(135deg,#2563eb,#1e40af);
  90. color:#fff;
  91. border:none;
  92. border-radius:12px;
  93. padding:12px 20px;
  94. font-weight:600;
  95. font-size:14px;
  96. cursor:pointer;
  97. transition:all .2s;
  98. box-shadow:0 4px 16px rgba(37,99,235,.3),inset 0 1px 0 rgba(255,255,255,.1);
  99. position:relative;
  100. overflow:hidden;
  101. }
  102. button:before {
  103. content:'';
  104. position:absolute;
  105. top:0;left:0;right:0;bottom:0;
  106. background:linear-gradient(135deg,rgba(255,255,255,.1),transparent);
  107. opacity:0;
  108. transition:opacity .2s;
  109. }
  110. button:hover {
  111. transform:translateY(-1px);
  112. box-shadow:0 6px 20px rgba(37,99,235,.4),inset 0 1px 0 rgba(255,255,255,.15);
  113. }
  114. button:hover:before { opacity:1; }
  115. button:active { transform:translateY(0); }
  116. button:disabled {
  117. opacity:.5;
  118. cursor:not-allowed;
  119. transform:none;
  120. }
  121. @keyframes slide-up {
  122. from { transform:translateY(100%); opacity:0; }
  123. to { transform:translateY(0); opacity:1; }
  124. }
  125. .toast {
  126. position:fixed;
  127. bottom:20px;
  128. right:20px;
  129. padding:12px 20px;
  130. border-radius:12px;
  131. color:#fff;
  132. font-size:14px;
  133. font-weight:500;
  134. box-shadow:0 8px 24px rgba(0,0,0,.4);
  135. animation:slide-up .3s ease-out;
  136. z-index:1000;
  137. }
  138. .toast.error {
  139. background:linear-gradient(135deg,#dc2626,#991b1b);
  140. }
  141. .toast.success {
  142. background:linear-gradient(135deg,#16a34a,#15803d);
  143. }
  144. </style>
  145. </head>
  146. <body>
  147. <div class="container">
  148. <div class="header">
  149. <h1>Q2API</h1>
  150. <p class="subtitle">Admin Console</p>
  151. </div>
  152. <div class="panel">
  153. <form id="loginForm">
  154. <div class="field">
  155. <label for="password">Password</label>
  156. <input type="password" id="password" name="password" required placeholder="Enter password" autofocus>
  157. </div>
  158. <button type="submit" id="loginButton">Login</button>
  159. </form>
  160. </div>
  161. </div>
  162. <script>
  163. const form=document.getElementById('loginForm'),btn=document.getElementById('loginButton');
  164. form.addEventListener('submit',async(e)=>{
  165. e.preventDefault();
  166. btn.disabled=true;
  167. btn.textContent='Logging in...';
  168. try{
  169. const fd=new FormData(form),r=await fetch('/api/login',{
  170. method:'POST',
  171. headers:{'Content-Type':'application/json'},
  172. body:JSON.stringify({password:fd.get('password')})
  173. });
  174. const d=await r.json();
  175. if(d.success){
  176. localStorage.setItem('adminPassword',fd.get('password'));
  177. location.href='/';
  178. }else{
  179. showToast(d.message||'Login failed','error');
  180. }
  181. }catch(e){
  182. showToast('Network error, please try again','error');
  183. }finally{
  184. btn.disabled=false;
  185. btn.textContent='Login';
  186. }
  187. });
  188. function showToast(m,t='error'){
  189. const d=document.createElement('div');
  190. d.className='toast '+t;
  191. d.textContent=m;
  192. document.body.appendChild(d);
  193. setTimeout(()=>{
  194. d.style.opacity='0';
  195. d.style.transition='opacity .3s';
  196. setTimeout(()=>d.parentNode&&document.body.removeChild(d),300)
  197. },2000);
  198. }
  199. </script>
  200. </body>
  201. </html>