mirror of Walter-Sparrow / lunar-tear
1<!DOCTYPE html>
2<html lang="en">
3<head>
4<meta charset="utf-8">
5<meta name="viewport" content="width=device-width, initial-scale=1">
6<title>Lunar Tear – Login</title>
7<style>
8 * { margin: 0; padding: 0; box-sizing: border-box; }
9 body {
10 font-family: 'Segoe UI', system-ui, -apple-system, sans-serif;
11 background: #0a0a0a;
12 color: #e0e0e0;
13 min-height: 100vh;
14 min-height: 100dvh;
15 display: flex;
16 align-items: center;
17 justify-content: center;
18 }
19 .card {
20 background: #161616;
21 border: 1px solid #2a2a2a;
22 border-radius: 8px;
23 padding: 40px 32px 32px;
24 width: 100%;
25 max-width: 360px;
26 }
27 h1 {
28 text-align: center;
29 font-size: 28px;
30 font-weight: 300;
31 letter-spacing: 6px;
32 text-transform: uppercase;
33 color: #c8c8c8;
34 margin-bottom: 8px;
35 }
36 .subtitle {
37 text-align: center;
38 font-size: 11px;
39 letter-spacing: 3px;
40 text-transform: uppercase;
41 color: #555;
42 margin-bottom: 32px;
43 transition: color 0.3s;
44 }
45 .error {
46 background: #2a1515;
47 border: 1px solid #5a2020;
48 color: #e08080;
49 border-radius: 4px;
50 padding: 10px 14px;
51 font-size: 13px;
52 margin-bottom: 20px;
53 }
54 label {
55 display: block;
56 font-size: 11px;
57 letter-spacing: 1px;
58 text-transform: uppercase;
59 color: #888;
60 margin-bottom: 6px;
61 }
62 input[type="text"],
63 input[type="password"] {
64 width: 100%;
65 padding: 10px 12px;
66 background: #0e0e0e;
67 border: 1px solid #333;
68 border-radius: 4px;
69 color: #e0e0e0;
70 font-size: 15px;
71 margin-bottom: 18px;
72 outline: none;
73 transition: border-color 0.2s;
74 }
75 input:focus { border-color: #666; }
76 .buttons {
77 display: flex;
78 gap: 10px;
79 margin-top: 8px;
80 }
81 button {
82 flex: 1;
83 padding: 11px 0;
84 border: 1px solid #333;
85 border-radius: 4px;
86 font-size: 13px;
87 letter-spacing: 1px;
88 cursor: pointer;
89 transition: background 0.2s, border-color 0.2s, opacity 0.3s;
90 }
91 .btn-login {
92 background: #e0e0e0;
93 color: #111;
94 border-color: #e0e0e0;
95 }
96 .btn-login:hover { background: #fff; border-color: #fff; }
97 .btn-register {
98 background: transparent;
99 color: #aaa;
100 }
101 .btn-register:hover { border-color: #666; color: #e0e0e0; }
102 .hidden { display: none; }
103 @media (max-height: 480px) {
104 body { align-items: stretch; padding: 0; }
105 .card {
106 max-width: none; border-radius: 0; border: none;
107 min-height: 100vh; min-height: 100dvh;
108 padding: 20px 24px;
109 display: flex; flex-direction: column; justify-content: center;
110 }
111 h1 { font-size: 22px; margin-bottom: 4px; }
112 .subtitle { margin-bottom: 16px; }
113 input[type="text"],
114 input[type="password"] { padding: 8px 10px; margin-bottom: 12px; }
115 .buttons { margin-top: 4px; }
116 button { padding: 9px 0; }
117 }
118</style>
119</head>
120<body>
121<form class="card" method="POST">
122 <h1>Lunar Tear</h1>
123 <div class="subtitle" id="subtitle">Authentication</div>
124
125 {{if .Error}}
126 <div class="error">{{.Error}}</div>
127 {{end}}
128
129 <input type="hidden" name="redirect_uri" value="{{.RedirectURI}}">
130 <input type="hidden" name="state" value="{{.State}}">
131 <input type="hidden" name="scope" value="{{.Scope}}">
132
133 <label for="username">Username</label>
134 <input type="text" id="username" name="username" value="{{.Username}}" autocomplete="username" autofocus required>
135
136 <label for="password">Password</label>
137 <input type="password" id="password" name="password" autocomplete="current-password" required>
138
139 <div class="buttons">
140 <button type="submit" name="action" value="login" class="btn-login hidden" id="btn-login">Login</button>
141 <button type="submit" name="action" value="register" class="btn-register hidden" id="btn-register">Create Account</button>
142 </div>
143</form>
144<script>
145(function() {
146 var input = document.getElementById('username');
147 var btnLogin = document.getElementById('btn-login');
148 var btnRegister = document.getElementById('btn-register');
149 var subtitle = document.getElementById('subtitle');
150 var timer = null;
151 var lastChecked = '';
152
153 function check() {
154 var name = input.value.trim();
155 if (name === '') {
156 btnLogin.classList.add('hidden');
157 btnRegister.classList.add('hidden');
158 subtitle.textContent = 'Authentication';
159 lastChecked = '';
160 return;
161 }
162 if (name === lastChecked) return;
163 lastChecked = name;
164
165 fetch('/check-username?username=' + encodeURIComponent(name))
166 .then(function(r) { return r.json(); })
167 .then(function(data) {
168 if (input.value.trim() !== name) return;
169 if (data.exists) {
170 btnLogin.classList.remove('hidden');
171 btnRegister.classList.add('hidden');
172 subtitle.textContent = 'Welcome back';
173 } else {
174 btnLogin.classList.add('hidden');
175 btnRegister.classList.remove('hidden');
176 subtitle.textContent = 'Create your account';
177 }
178 })
179 .catch(function() {
180 btnLogin.classList.remove('hidden');
181 btnRegister.classList.remove('hidden');
182 subtitle.textContent = 'Authentication';
183 });
184 }
185
186 input.addEventListener('input', function() {
187 clearTimeout(timer);
188 timer = setTimeout(check, 300);
189 });
190
191 input.addEventListener('blur', function() {
192 clearTimeout(timer);
193 check();
194 });
195
196 if (input.value.trim() !== '') check();
197})();
198</script>
199</body>
200</html>