ai added simple ui for register and login

This commit is contained in:
2026-03-07 21:14:50 +01:00
parent cae24c5c0c
commit 533fcaa498
9 changed files with 248 additions and 2 deletions
+10
View File
@@ -0,0 +1,10 @@
# Default ignored files
/shelf/
/workspace.xml
# Ignored default folder with query files
/queries/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml
# Editor-based HTTP Client requests
/httpRequests/
+22
View File
@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="InertiaPackage">
<option name="directoryPaths">
<list />
</option>
</component>
<component name="LaravelIdeaMainSettings">
<option name="codeGeneration">
<LaravelCodeGeneration>
<option name="generationStringSettings">
<map>
<entry key="createEloquentScope:namespace" value="Models\Scopes" />
<entry key="createModel:namespace" value="Models" />
</map>
</option>
</LaravelCodeGeneration>
</option>
<option name="frameworkFound" value="true" />
<option name="userClassName" value="\App\Models\User" />
</component>
</project>
+8
View File
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/php-com-cen.iml" filepath="$PROJECT_DIR$/.idea/php-com-cen.iml" />
</modules>
</component>
</project>
+29
View File
@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" packagePrefix="ComCen\" />
<excludeFolder url="file://$MODULE_DIR$/vendor/cboden/ratchet" />
<excludeFolder url="file://$MODULE_DIR$/vendor/composer" />
<excludeFolder url="file://$MODULE_DIR$/vendor/evenement/evenement" />
<excludeFolder url="file://$MODULE_DIR$/vendor/guzzlehttp/psr7" />
<excludeFolder url="file://$MODULE_DIR$/vendor/psr/http-factory" />
<excludeFolder url="file://$MODULE_DIR$/vendor/psr/http-message" />
<excludeFolder url="file://$MODULE_DIR$/vendor/ralouphie/getallheaders" />
<excludeFolder url="file://$MODULE_DIR$/vendor/ratchet/rfc6455" />
<excludeFolder url="file://$MODULE_DIR$/vendor/react/cache" />
<excludeFolder url="file://$MODULE_DIR$/vendor/react/dns" />
<excludeFolder url="file://$MODULE_DIR$/vendor/react/event-loop" />
<excludeFolder url="file://$MODULE_DIR$/vendor/react/promise" />
<excludeFolder url="file://$MODULE_DIR$/vendor/react/socket" />
<excludeFolder url="file://$MODULE_DIR$/vendor/react/stream" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/deprecation-contracts" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/http-foundation" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/polyfill-mbstring" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/polyfill-php83" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/routing" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>
Generated
+45
View File
@@ -0,0 +1,45 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="MessDetectorOptionsConfiguration">
<option name="transferred" value="true" />
</component>
<component name="PHPCSFixerOptionsConfiguration">
<option name="transferred" value="true" />
</component>
<component name="PHPCodeSnifferOptionsConfiguration">
<option name="highlightLevel" value="WARNING" />
<option name="transferred" value="true" />
</component>
<component name="PhpIncludePathManager">
<include_path>
<path value="$PROJECT_DIR$/vendor/psr/http-factory" />
<path value="$PROJECT_DIR$/vendor/psr/http-message" />
<path value="$PROJECT_DIR$/vendor/symfony/deprecation-contracts" />
<path value="$PROJECT_DIR$/vendor/guzzlehttp/psr7" />
<path value="$PROJECT_DIR$/vendor/react/promise" />
<path value="$PROJECT_DIR$/vendor/symfony/polyfill-mbstring" />
<path value="$PROJECT_DIR$/vendor/react/cache" />
<path value="$PROJECT_DIR$/vendor/react/dns" />
<path value="$PROJECT_DIR$/vendor/react/stream" />
<path value="$PROJECT_DIR$/vendor/react/event-loop" />
<path value="$PROJECT_DIR$/vendor/react/socket" />
<path value="$PROJECT_DIR$/vendor/symfony/polyfill-php83" />
<path value="$PROJECT_DIR$/vendor/ratchet/rfc6455" />
<path value="$PROJECT_DIR$/vendor/ralouphie/getallheaders" />
<path value="$PROJECT_DIR$/vendor/cboden/ratchet" />
<path value="$PROJECT_DIR$/vendor/evenement/evenement" />
<path value="$PROJECT_DIR$/vendor/symfony/routing" />
<path value="$PROJECT_DIR$/vendor/composer" />
<path value="$PROJECT_DIR$/vendor/symfony/http-foundation" />
</include_path>
</component>
<component name="PhpProjectSharedConfiguration" php_language_level="8.0">
<option name="suggestChangeDefaultLanguageLevel" value="false" />
</component>
<component name="PhpStanOptionsConfiguration">
<option name="transferred" value="true" />
</component>
<component name="PsalmOptionsConfiguration">
<option name="transferred" value="true" />
</component>
</project>
Generated
+6
View File
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>
+64 -1
View File
@@ -13,10 +13,17 @@ class LoginController implements HttpServerInterface
public function onOpen(ConnectionInterface $conn, RequestInterface $request = null): void
{
$params = [];
parse_str($request->getUri()->getQuery(), $params);
if (empty($params["username"]) && empty($params["password"])) {
Utils::respondHtml($conn, $this->loginPage());
$conn->close();
return;
}
$login = true;
$responseHead = "";
$json = "";
parse_str($request->getUri()->getQuery(), $params);
$username = $params["username"];
$password = $params["password"];
@@ -49,6 +56,62 @@ class LoginController implements HttpServerInterface
$conn->close();
}
private function loginPage(): string
{
return <<<'HTML'
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Login — ComCen</title>
<style>
body { font-family: monospace; max-width: 360px; margin: 80px auto; padding: 0 16px; }
h2 { margin-bottom: 24px; }
input { width: 100%; box-sizing: border-box; padding: 6px; font-family: monospace; font-size: 14px; margin-bottom: 10px; border: 1px solid #ccc; }
button { padding: 6px 16px; font-family: monospace; font-size: 14px; cursor: pointer; }
#msg { margin-top: 12px; font-size: 13px; }
.error { color: #c00; }
#token-box { margin-top: 16px; display: none; }
#token-box p { margin: 0 0 6px; }
#token-val { width: 100%; box-sizing: border-box; padding: 6px; font-family: monospace; font-size: 12px; background: #f4f4f4; border: 1px solid #ccc; }
a { color: inherit; }
</style>
</head>
<body>
<h2>Login</h2>
<input id="username" type="text" placeholder="Username" />
<input id="password" type="password" placeholder="Password" />
<button onclick="login()">Login</button>
<div id="msg"></div>
<div id="token-box">
<p>Your token (paste into chat):</p>
<input id="token-val" type="text" readonly onclick="this.select()" />
</div>
<p><a href="/register">No account? Register</a></p>
<script>
async function login() {
const u = document.getElementById('username').value.trim();
const p = document.getElementById('password').value.trim();
const msg = document.getElementById('msg');
if (!u || !p) { msg.className = 'error'; msg.textContent = 'Fill in all fields.'; return; }
msg.textContent = '';
const res = await fetch('/login?username=' + encodeURIComponent(u) + '&password=' + encodeURIComponent(p));
const data = await res.json();
if (data.token) {
document.getElementById('token-val').value = data.token;
document.getElementById('token-box').style.display = 'block';
} else {
msg.className = 'error';
msg.textContent = data.error || 'Login failed.';
}
}
document.addEventListener('keydown', e => { if (e.key === 'Enter') login(); });
</script>
</body>
</html>
HTML;
}
public function onMessage(ConnectionInterface $from, $msg): void {}
public function onClose(ConnectionInterface $conn): void {}
public function onError(ConnectionInterface $conn, \Exception $e): void { $conn->close(); }
+59 -1
View File
@@ -12,10 +12,17 @@ class RegisterController implements HttpServerInterface
public function onOpen(ConnectionInterface $conn, RequestInterface $request = null): void
{
$params = [];
parse_str($request->getUri()->getQuery(), $params);
if (empty($params["username"]) && empty($params["password"])) {
Utils::respondHtml($conn, $this->registerPage());
$conn->close();
return;
}
$createAccount = true;
$responseHead = "";
$json = "";
parse_str($request->getUri()->getQuery(), $params);
$username = $params["username"];
$password = $params["password"];
@@ -53,6 +60,57 @@ class RegisterController implements HttpServerInterface
$conn->close();
}
private function registerPage(): string
{
return <<<'HTML'
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Register — ComCen</title>
<style>
body { font-family: monospace; max-width: 360px; margin: 80px auto; padding: 0 16px; }
h2 { margin-bottom: 24px; }
input { width: 100%; box-sizing: border-box; padding: 6px; font-family: monospace; font-size: 14px; margin-bottom: 10px; border: 1px solid #ccc; }
button { padding: 6px 16px; font-family: monospace; font-size: 14px; cursor: pointer; }
#msg { margin-top: 12px; font-size: 13px; }
.error { color: #c00; }
.success { color: #080; }
a { color: inherit; }
</style>
</head>
<body>
<h2>Register</h2>
<input id="username" type="text" placeholder="Username" />
<input id="password" type="password" placeholder="Password (min 5 chars)" />
<button onclick="register()">Register</button>
<div id="msg"></div>
<p><a href="/login">Already have an account? Login</a></p>
<script>
async function register() {
const u = document.getElementById('username').value.trim();
const p = document.getElementById('password').value.trim();
const msg = document.getElementById('msg');
if (!u || !p) { msg.className = 'error'; msg.textContent = 'Fill in all fields.'; return; }
msg.textContent = '';
const res = await fetch('/register?username=' + encodeURIComponent(u) + '&password=' + encodeURIComponent(p));
const data = await res.json();
if (data.error === 'none') {
msg.className = 'success';
msg.textContent = 'Account created! Redirecting to login...';
setTimeout(() => window.location.href = '/login', 1200);
} else {
msg.className = 'error';
msg.textContent = data.error || 'Registration failed.';
}
}
document.addEventListener('keydown', e => { if (e.key === 'Enter') register(); });
</script>
</body>
</html>
HTML;
}
public function onMessage(ConnectionInterface $from, $msg): void {}
public function onClose(ConnectionInterface $conn): void {}
public function onError(ConnectionInterface $conn, \Exception $e): void { $conn->close(); }
+5
View File
@@ -10,4 +10,9 @@ class Utils
{
$conn->send("HTTP/1.1 {$head}\r\nContent-Type: application/json\r\n\r\n{$jsonData}");
}
static function respondHtml(ConnectionInterface $conn, string $html): void
{
$conn->send("HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n{$html}");
}
}