Updsate
This commit is contained in:
parent
dbeb74692d
commit
717669245d
145
Login1.html
145
Login1.html
@ -1,11 +1,13 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="stylesheet" href="Login1.css">
|
||||
<title>Space Odyssey</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="stars"></div>
|
||||
<div class="neon-grid"></div>
|
||||
@ -14,66 +16,149 @@
|
||||
<div class="left-panel">
|
||||
<div class="title-container">
|
||||
<h1 class="main-title">SPACE<br>ODYSSEY</h1>
|
||||
<div class="subtitle">Initializing Spaceship</div>
|
||||
<div class="subtitle" id="subtitle">Initializing Spaceship</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="right-panel">
|
||||
<div class="form-box">
|
||||
<div class="form-content">
|
||||
<h2 class="form-title">Access Terminal</h2>
|
||||
<!-- box login-->
|
||||
<label for="username">Username</label>
|
||||
<input type="text" id="username" placeholder="Enter callsign...">
|
||||
<h2 class="form-title" id="formTitle">Access Terminal</h2>
|
||||
|
||||
<label for="username" id="loginLabel">Username</label>
|
||||
<input type="text" id="username" placeholder="Enter callsign..." autocomplete="username">
|
||||
|
||||
<label for="password">Password</label>
|
||||
<input type="password" id="password" placeholder="Enter access code...">
|
||||
<input type="password" id="password" placeholder="Enter access code..."
|
||||
autocomplete="current-password">
|
||||
|
||||
<div class="button-group">
|
||||
<button class="btn-send" onclick="registerUser()">Connect</button>
|
||||
<button class="btn-send" type="button" id="connectBtn">Connect</button>
|
||||
<div class="signup-text">
|
||||
<!-- link belm selsai-->
|
||||
Don't have a terminal? <a href="#">Sign up</a>
|
||||
<span id="toggleText">Don't have a terminal?</span> <a href="#" id="toggleLink">Sign up</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="user-list" id="userList"></div>
|
||||
|
||||
<script>
|
||||
let mode = 'login';
|
||||
let userCounter = 0;
|
||||
|
||||
function registerUser() {
|
||||
const username = document.getElementById('username').value.trim();
|
||||
const password = document.getElementById('password').value;
|
||||
|
||||
if (!username || !password) {
|
||||
alert('ya begitulah');
|
||||
return;
|
||||
}
|
||||
const els = {
|
||||
subtitle: document.getElementById('subtitle'),
|
||||
formTitle: document.getElementById('formTitle'),
|
||||
loginLabel: document.getElementById('loginLabel'),
|
||||
username: document.getElementById('username'),
|
||||
password: document.getElementById('password'),
|
||||
connectBtn: document.getElementById('connectBtn'),
|
||||
toggleText: document.getElementById('toggleText'),
|
||||
toggleLink: document.getElementById('toggleLink'),
|
||||
userList: document.getElementById('userList'),
|
||||
};
|
||||
|
||||
function showToast(htmlText) {
|
||||
const userId = 'user_' + userCounter++;
|
||||
|
||||
const userCard = document.createElement('div');
|
||||
userCard.className = 'user-card';
|
||||
userCard.id = userId;
|
||||
userCard.innerHTML = `
|
||||
<div class="user-name">✓ You have logged on as <strong>${username}</strong></div>
|
||||
`;
|
||||
|
||||
document.getElementById('userList').appendChild(userCard);
|
||||
|
||||
setTimeout(() => {
|
||||
userCard.classList.add('usercardfade-out');
|
||||
userCard.innerHTML = `<div class="user-name">${htmlText}</div>`;
|
||||
els.userList.appendChild(userCard);
|
||||
|
||||
setTimeout(() => {
|
||||
userCard.remove();
|
||||
}, 500);
|
||||
userCard.classList.add('usercardfade-out');
|
||||
setTimeout(() => userCard.remove(), 500);
|
||||
}, 3000);
|
||||
}
|
||||
|
||||
}, 3000);
|
||||
function setMode(nextMode) {
|
||||
mode = nextMode;
|
||||
|
||||
};
|
||||
if (mode === 'login') {
|
||||
els.formTitle.textContent = 'Access Terminal';
|
||||
els.subtitle.textContent = 'Initializing Spaceship';
|
||||
els.loginLabel.textContent = 'Username';
|
||||
els.username.placeholder = 'Enter callsign...';
|
||||
els.password.autocomplete = 'current-password';
|
||||
els.connectBtn.textContent = 'Connect';
|
||||
els.toggleText.textContent = "Don't have a terminal?";
|
||||
els.toggleLink.textContent = 'Sign up';
|
||||
} else {
|
||||
els.formTitle.textContent = 'Create Terminal';
|
||||
els.subtitle.textContent = 'Registering Pilot';
|
||||
els.loginLabel.textContent = 'Username';
|
||||
els.username.placeholder = 'Choose callsign...';
|
||||
els.password.autocomplete = 'new-password';
|
||||
els.connectBtn.textContent = 'Register';
|
||||
els.toggleText.textContent = 'Already have a terminal?';
|
||||
els.toggleLink.textContent = 'Log in';
|
||||
}
|
||||
}
|
||||
|
||||
async function postJson(url, payload) {
|
||||
const res = await fetch(url, {
|
||||
method: 'POST',
|
||||
credentials: 'include',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(payload),
|
||||
});
|
||||
|
||||
let data = null;
|
||||
try { data = await res.json(); } catch (_) { }
|
||||
if (!res.ok) {
|
||||
const msg = (data && data.error) ? data.error : `Request failed (${res.status})`;
|
||||
throw new Error(msg);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
async function submitAuth() {
|
||||
const username = els.username.value.trim();
|
||||
const password = els.password.value;
|
||||
|
||||
if (!username || !password) {
|
||||
showToast('✗ Please enter <strong>username</strong> and <strong>password</strong>.');
|
||||
return;
|
||||
}
|
||||
|
||||
els.connectBtn.disabled = true;
|
||||
|
||||
try {
|
||||
if (mode === 'login') {
|
||||
const out = await postJson('./api/login.php', { login: username, password });
|
||||
showToast(`✓ You have logged on as <strong>${out.user.username}</strong>`);
|
||||
setTimeout(() => { window.location.href = './Main.html'; }, 700);
|
||||
} else {
|
||||
const out = await postJson('./api/register.php', { username, password });
|
||||
showToast(`✓ Terminal created for <strong>${out.user.username}</strong>`);
|
||||
setTimeout(() => { window.location.href = './Main.html'; }, 700);
|
||||
}
|
||||
} catch (e) {
|
||||
showToast(`✗ <strong>${e.message}</strong>`);
|
||||
} finally {
|
||||
els.connectBtn.disabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
els.connectBtn.addEventListener('click', submitAuth);
|
||||
els.toggleLink.addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
setMode(mode === 'login' ? 'register' : 'login');
|
||||
els.password.value = '';
|
||||
els.password.focus();
|
||||
});
|
||||
|
||||
document.addEventListener('keydown', (e) => {
|
||||
if (e.key === 'Enter') submitAuth();
|
||||
});
|
||||
|
||||
setMode('login');
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
34
api/config.php
Normal file
34
api/config.php
Normal file
@ -0,0 +1,34 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
session_start();
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
|
||||
$DB_HOST = '127.0.0.1';
|
||||
$DB_NAME = 'space_odyssey';
|
||||
$DB_USER = 'root';
|
||||
$DB_PASS = '';
|
||||
|
||||
function json_out(int $status, array $data): void {
|
||||
http_response_code($status);
|
||||
echo json_encode($data, JSON_UNESCAPED_UNICODE);
|
||||
exit;
|
||||
}
|
||||
|
||||
try {
|
||||
$dsn = "mysql:host={$DB_HOST};dbname={$DB_NAME};charset=utf8mb4";
|
||||
$pdo = new PDO($dsn, $DB_USER, $DB_PASS, [
|
||||
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
|
||||
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
|
||||
]);
|
||||
} catch (Throwable $e) {
|
||||
json_out(500, ['ok' => false, 'error' => 'DB connection failed']);
|
||||
}
|
||||
|
||||
function current_user(PDO $pdo): ?array {
|
||||
if (!isset($_SESSION['user_id'])) return null;
|
||||
$stmt = $pdo->prepare('SELECT id, username, email, created_at FROM users WHERE id = ? LIMIT 1');
|
||||
$stmt->execute([(int)$_SESSION['user_id']]);
|
||||
$u = $stmt->fetch();
|
||||
return $u ?: null;
|
||||
}
|
||||
33
api/login.php
Normal file
33
api/login.php
Normal file
@ -0,0 +1,33 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
require __DIR__ . '/config.php';
|
||||
|
||||
$data = json_decode(file_get_contents('php://input') ?: '[]', true);
|
||||
if (!is_array($data)) json_out(400, ['ok' => false, 'error' => 'Invalid JSON']);
|
||||
|
||||
$login = isset($data['login']) ? trim((string)$data['login']) : '';
|
||||
$password = isset($data['password']) ? (string)$data['password'] : '';
|
||||
|
||||
if ($login === '' || $password === '') {
|
||||
json_out(400, ['ok' => false, 'error' => 'Missing login or password']);
|
||||
}
|
||||
|
||||
$stmt = $pdo->prepare('SELECT id, username, email, password_hash, created_at FROM users WHERE username = ? OR email = ? LIMIT 1');
|
||||
$stmt->execute([$login, $login]);
|
||||
$user = $stmt->fetch();
|
||||
|
||||
if (!$user || !password_verify($password, (string)$user['password_hash'])) {
|
||||
json_out(401, ['ok' => false, 'error' => 'Invalid credentials']);
|
||||
}
|
||||
|
||||
$_SESSION['user_id'] = (int)$user['id'];
|
||||
|
||||
json_out(200, [
|
||||
'ok' => true,
|
||||
'user' => [
|
||||
'id' => (int)$user['id'],
|
||||
'username' => (string)$user['username'],
|
||||
'email' => $user['email'],
|
||||
'created_at' => $user['created_at'],
|
||||
]
|
||||
]);
|
||||
12
api/logout.php
Normal file
12
api/logout.php
Normal file
@ -0,0 +1,12 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
require __DIR__ . '/config.php';
|
||||
|
||||
$_SESSION = [];
|
||||
if (ini_get('session.use_cookies')) {
|
||||
$p = session_get_cookie_params();
|
||||
setcookie(session_name(), '', time() - 42000, $p['path'], $p['domain'], (bool)$p['secure'], (bool)$p['httponly']);
|
||||
}
|
||||
session_destroy();
|
||||
|
||||
json_out(200, ['ok' => true]);
|
||||
6
api/me.php
Normal file
6
api/me.php
Normal file
@ -0,0 +1,6 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
require __DIR__ . '/config.php';
|
||||
|
||||
$u = current_user($pdo);
|
||||
json_out(200, ['ok' => true, 'user' => $u]);
|
||||
41
api/register.php
Normal file
41
api/register.php
Normal file
@ -0,0 +1,41 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
require __DIR__ . '/config.php';
|
||||
|
||||
$data = json_decode(file_get_contents('php://input') ?: '[]', true);
|
||||
if (!is_array($data)) json_out(400, ['ok' => false, 'error' => 'Invalid JSON']);
|
||||
|
||||
$username = isset($data['username']) ? trim((string)$data['username']) : '';
|
||||
$email = isset($data['email']) ? trim((string)$data['email']) : '';
|
||||
$password = isset($data['password']) ? (string)$data['password'] : '';
|
||||
|
||||
if ($username === '' || strlen($username) < 3 || strlen($username) > 32) {
|
||||
json_out(400, ['ok' => false, 'error' => 'Username must be 3-32 chars']);
|
||||
}
|
||||
if (!preg_match('/^[A-Za-z0-9_]+$/', $username)) {
|
||||
json_out(400, ['ok' => false, 'error' => 'Username must be letters/numbers/_ only']);
|
||||
}
|
||||
if ($email !== '' && !filter_var($email, FILTER_VALIDATE_EMAIL)) {
|
||||
json_out(400, ['ok' => false, 'error' => 'Invalid email']);
|
||||
}
|
||||
if (strlen($password) < 6) {
|
||||
json_out(400, ['ok' => false, 'error' => 'Password must be at least 6 chars']);
|
||||
}
|
||||
|
||||
$hash = password_hash($password, PASSWORD_DEFAULT);
|
||||
|
||||
try {
|
||||
$stmt = $pdo->prepare('INSERT INTO users (username, email, password_hash) VALUES (?, ?, ?)');
|
||||
$stmt->execute([$username, ($email === '' ? null : $email), $hash]);
|
||||
|
||||
$_SESSION['user_id'] = (int)$pdo->lastInsertId();
|
||||
$u = current_user($pdo);
|
||||
|
||||
json_out(201, ['ok' => true, 'user' => $u]);
|
||||
} catch (Throwable $e) {
|
||||
$msg = $e->getMessage();
|
||||
if (stripos($msg, 'Duplicate') !== false || stripos($msg, 'uq_') !== false) {
|
||||
json_out(409, ['ok' => false, 'error' => 'Username or email already used']);
|
||||
}
|
||||
json_out(500, ['ok' => false, 'error' => 'Register failed']);
|
||||
}
|
||||
BIN
img/module_table_bottom.png
Normal file
BIN
img/module_table_bottom.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 751 B |
BIN
img/module_table_top.png
Normal file
BIN
img/module_table_top.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 337 B |
Loading…
x
Reference in New Issue
Block a user