434 lines
14 KiB
HTML
434 lines
14 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>黑盒智能开发者平台</title>
|
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css" integrity="sha512-iBBXm8fW90+nuLcSKlbmrPcLa0OT92xO1BIsZ+ywDWZCvqsWgccV3gFoRBv0z+8dLJgyAHIhR35VZc2oM/gI1w==" crossorigin="anonymous" referrerpolicy="no-referrer" />
|
|
<style>
|
|
* {
|
|
box-sizing: border-box;
|
|
margin: 0;
|
|
padding: 0;
|
|
font-family: 'Arial', sans-serif;
|
|
}
|
|
body {
|
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
min-height: 100vh;
|
|
display: flex;
|
|
flex-direction: column;
|
|
justify-content: center;
|
|
align-items: center;
|
|
padding: 20px;
|
|
}
|
|
|
|
.header {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
margin-bottom: 20px;
|
|
}
|
|
|
|
.container {
|
|
background-color: rgba(255, 255, 255, 0.9);
|
|
border-radius: 15px;
|
|
box-shadow: 0 14px 28px rgba(0,0,0,0.25), 0 10px 10px rgba(0,0,0,0.22);
|
|
position: relative;
|
|
overflow: hidden;
|
|
width: 80%;
|
|
max-width: 1200px; /* Set a maximum width */
|
|
min-height: 900px; /* Use a fixed minimum height */
|
|
margin: 20px auto; /* Add some margin */
|
|
}
|
|
.form-container {
|
|
position: absolute;
|
|
top: 0;
|
|
height: 100%;
|
|
transition: all 0.6s ease-in-out;
|
|
}
|
|
.sign-in-container {
|
|
left: 0;
|
|
width: 50%;
|
|
z-index: 2;
|
|
}
|
|
.sign-up-container {
|
|
left: 0;
|
|
width: 50%;
|
|
opacity: 0;
|
|
z-index: 1;
|
|
}
|
|
.container.right-panel-active .sign-in-container {
|
|
transform: translateX(100%);
|
|
}
|
|
.container.right-panel-active .sign-up-container {
|
|
transform: translateX(100%);
|
|
opacity: 1;
|
|
z-index: 5;
|
|
}
|
|
form {
|
|
background-color: #FFFFFF;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
flex-direction: column;
|
|
padding: 0 50px;
|
|
height: 100%;
|
|
text-align: center;
|
|
}
|
|
h1 {
|
|
font-weight: bold;
|
|
margin-bottom: 30px;
|
|
font-size: 3.5em;
|
|
}
|
|
|
|
p {
|
|
font-size: 1.2em;
|
|
margin-bottom: 30px; /* Increase bottom margin */
|
|
}
|
|
|
|
|
|
input {
|
|
background-color: #eee;
|
|
border: none;
|
|
padding: 12px 15px;
|
|
margin: 15px 0;
|
|
width: 100%;
|
|
border-radius: 10px;
|
|
}
|
|
|
|
button {
|
|
border-radius: 50px;
|
|
border: 1px solid #667eea;
|
|
background-color: #667eea;
|
|
color: #FFFFFF;
|
|
font-size: 20px;
|
|
font-weight: bold;
|
|
padding: 12px 45px;
|
|
letter-spacing: 3px;
|
|
text-transform: uppercase;
|
|
transition: transform 80ms ease-in;
|
|
margin-bottom: 50px;
|
|
}
|
|
button:active {
|
|
transform: scale(0.95);
|
|
}
|
|
button:focus {
|
|
outline: none;
|
|
}
|
|
button.ghost {
|
|
background-color: transparent;
|
|
border-color: #FFFFFF;
|
|
}
|
|
.social-container {
|
|
margin: 20px 0;
|
|
}
|
|
.social-container a {
|
|
border: 1px solid #DDDDDD;
|
|
border-radius: 50%;
|
|
display: inline-flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
margin: 0 5px;
|
|
height: 40px;
|
|
width: 40px;
|
|
}
|
|
.overlay-container {
|
|
position: absolute;
|
|
top: 0;
|
|
left: 50%;
|
|
width: 50%;
|
|
height: 100%;
|
|
overflow: hidden;
|
|
transition: transform 0.6s ease-in-out;
|
|
z-index: 100;
|
|
}
|
|
.container.right-panel-active .overlay-container{
|
|
transform: translateX(-100%);
|
|
}
|
|
.overlay {
|
|
background: #667eea;
|
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
background-repeat: no-repeat;
|
|
background-size: cover;
|
|
background-position: 0 0;
|
|
color: #FFFFFF;
|
|
position: relative;
|
|
left: -100%;
|
|
height: 100%;
|
|
width: 200%;
|
|
transform: translateX(0);
|
|
transition: transform 0.6s ease-in-out;
|
|
}
|
|
.container.right-panel-active .overlay {
|
|
transform: translateX(50%);
|
|
}
|
|
.overlay-panel {
|
|
position: absolute;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
flex-direction: column;
|
|
padding: 0 40px;
|
|
text-align: center;
|
|
top: 0;
|
|
height: 100%;
|
|
width: 50%;
|
|
transform: translateX(0);
|
|
transition: transform 0.6s ease-in-out;
|
|
}
|
|
.overlay-left {
|
|
transform: translateX(-20%);
|
|
}
|
|
.container.right-panel-active .overlay-left {
|
|
transform: translateX(0);
|
|
}
|
|
.overlay-right {
|
|
right: 0;
|
|
transform: translateX(0);
|
|
}
|
|
.container.right-panel-active .overlay-right {
|
|
transform: translateX(20%);
|
|
}
|
|
|
|
@media (max-width: 768px) {
|
|
.container {
|
|
width: 90%;
|
|
min-height: 90vh;
|
|
}
|
|
form {
|
|
padding: 0 20px;
|
|
}
|
|
.overlay-panel {
|
|
padding: 0 20px;
|
|
}
|
|
.title {
|
|
font-size: 2em;
|
|
}
|
|
.logo {
|
|
font-size: 2.5em;
|
|
}
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="container" id="container">
|
|
<div class="form-container sign-up-container">
|
|
<form id="signupForm">
|
|
<h1>Create Account</h1>
|
|
<!-- <div class="social-container">
|
|
<a href="#" class="social"><i class="fab fa-google"></i></a>
|
|
<a href="#" class="social"><i class="fab fa-github"></i></a>
|
|
</div> -->
|
|
<span>use your email for registration</span>
|
|
<input type="text" placeholder="Username" id="signupUsername" required />
|
|
<input type="password" placeholder="Password" id="signupPassword" required />
|
|
<div class="password-rules">
|
|
Password must be at least 8 characters long and include uppercase, lowercase, digit, and special character (!@#$%^&*?:).
|
|
</div>
|
|
<input type="email" placeholder="Email" id="signupEmail" required />
|
|
<input type="tel" placeholder="Phone" id="signupPhone" required />
|
|
<button type="submit">REGISTER</button>
|
|
</form>
|
|
</div>
|
|
<div class="form-container sign-in-container">
|
|
<form id="signinForm">
|
|
<h1>Sign in</h1>
|
|
<!-- <div class="social-container">
|
|
<a href="#" class="social"><i class="fab fa-google"></i></a>
|
|
<a href="#" class="social"><i class="fab fa-github"></i></a>
|
|
</div> -->
|
|
<span>use your account</span>
|
|
|
|
<input type="text" placeholder="Username" id="signinUsername" required />
|
|
<input type="password" placeholder="Password" id="signinPassword" required />
|
|
<a href="#">Forgot your password?</a>
|
|
<button type="submit">Sign In</button>
|
|
</form>
|
|
</div>
|
|
<div class="overlay-container">
|
|
<div class="overlay">
|
|
<div class="overlay-panel overlay-left">
|
|
<h1>Welcome Back!</h1>
|
|
<p>To keep connected with us please login with your personal info</p>
|
|
<button class="ghost" id="signIn">Sign In</button>
|
|
</div>
|
|
<div class="overlay-panel overlay-right">
|
|
<h1>Hello, Friend!</h1>
|
|
<p>Start your journey with us</p>
|
|
<button class="ghost" id="signUp">Sign Up</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
const signUpButton = document.getElementById('signUp');
|
|
const signInButton = document.getElementById('signIn');
|
|
const container = document.getElementById('container');
|
|
|
|
signUpButton.addEventListener('click', () => {
|
|
container.classList.add("right-panel-active");
|
|
});
|
|
|
|
signInButton.addEventListener('click', () => {
|
|
container.classList.remove("right-panel-active");
|
|
});
|
|
|
|
const API_URL = 'https://user.obscura.work/user';
|
|
|
|
// 页面加载时检查登录状态
|
|
document.addEventListener('DOMContentLoaded', checkLoginStatus);
|
|
|
|
function checkLoginStatus() {
|
|
const token = localStorage.getItem('access_token');
|
|
if (token) {
|
|
validateToken(token);
|
|
} else {
|
|
showLoginForm();
|
|
}
|
|
}
|
|
|
|
async function validateToken(token) {
|
|
try {
|
|
const response = await fetch(`${API_URL}/me`, {
|
|
headers: {
|
|
'Authorization': `Bearer ${token}`,
|
|
},
|
|
});
|
|
|
|
if (response.ok) {
|
|
const data = await response.json();
|
|
console.log('用户信息:', data);
|
|
redirectToDashboard();
|
|
} else {
|
|
throw new Error('无效的token');
|
|
}
|
|
} catch (error) {
|
|
console.error('错误:', error);
|
|
logout();
|
|
}
|
|
}
|
|
|
|
function showLoginForm() {
|
|
container.classList.remove("right-panel-active");
|
|
}
|
|
|
|
function redirectToDashboard(token) {
|
|
window.location.href = `https://user.obscura.work/platform.html`;
|
|
}
|
|
|
|
async function logout() {
|
|
const token = localStorage.getItem('access_token');
|
|
if (token) {
|
|
try {
|
|
await fetch(`${API_URL}/logout`, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Authorization': `Bearer ${token}`,
|
|
},
|
|
});
|
|
} catch (error) {
|
|
console.error('登出时发生错误:', error);
|
|
}
|
|
}
|
|
localStorage.removeItem('access_token');
|
|
showLoginForm();
|
|
}
|
|
|
|
// 注册
|
|
document.getElementById('signupForm').addEventListener('submit', async (e) => {
|
|
e.preventDefault();
|
|
const username = document.getElementById('signupUsername').value;
|
|
const password = document.getElementById('signupPassword').value;
|
|
const email = document.getElementById('signupEmail').value;
|
|
const phoneNumber = document.getElementById('signupPhone').value;
|
|
|
|
try {
|
|
const response = await fetch(`${API_URL}/register`, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: JSON.stringify({
|
|
username: username,
|
|
email: email,
|
|
phone_number: phoneNumber,
|
|
password: password
|
|
}),
|
|
});
|
|
|
|
const data = await response.json();
|
|
if (response.ok) {
|
|
alert(data.message);
|
|
showLoginForm();
|
|
} else {
|
|
let errorMessage = '注册失败: ';
|
|
if (typeof data.detail === 'object') {
|
|
for (let key in data.detail) {
|
|
errorMessage += `${key}: ${data.detail[key].join(', ')}\n`;
|
|
}
|
|
} else {
|
|
errorMessage += data.detail || '发生未知错误';
|
|
}
|
|
alert(errorMessage);
|
|
}
|
|
} catch (error) {
|
|
console.error('错误:', error);
|
|
alert('发生错误,请重试。');
|
|
}
|
|
});
|
|
|
|
// 登录
|
|
document.getElementById('signinForm').addEventListener('submit', async (e) => {
|
|
e.preventDefault();
|
|
const username = document.getElementById('signinUsername').value;
|
|
const password = document.getElementById('signinPassword').value;
|
|
|
|
try {
|
|
const response = await fetch(`${API_URL}/token`, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/x-www-form-urlencoded',
|
|
},
|
|
body: `username=${encodeURIComponent(username)}&password=${encodeURIComponent(password)}`,
|
|
});
|
|
|
|
const data = await response.json();
|
|
if (response.ok) {
|
|
localStorage.setItem('access_token', data.access_token);
|
|
alert('登录成功');
|
|
redirectToDashboard(data.access_token);
|
|
} else {
|
|
alert(data.detail || '登录失败');
|
|
}
|
|
} catch (error) {
|
|
console.error('错误:', error);
|
|
alert('发生错误,请重试。');
|
|
}
|
|
});
|
|
|
|
// 处理需要认证的API请求
|
|
async function makeAuthenticatedRequest(url, options = {}) {
|
|
const token = localStorage.getItem('access_token');
|
|
if (!token) {
|
|
throw new Error('未找到token');
|
|
}
|
|
|
|
const headers = new Headers(options.headers || {});
|
|
headers.append('Authorization', `Bearer ${token}`);
|
|
|
|
const response = await fetch(url, { ...options, headers });
|
|
|
|
if (response.status === 401) {
|
|
logout();
|
|
throw new Error('未授权');
|
|
}
|
|
|
|
return response;
|
|
}
|
|
|
|
|
|
</script>
|
|
</body>
|
|
</html>
|