Files
sisai-world/views/user.html
2025-10-06 21:31:13 +09:00

676 lines
24 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html
lang="en-US"
class="light-style"
>
<head>
<meta charset="utf-8" />
<meta
name="viewport"
content="width=device-width, initial-scale=1.0, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0"
/>
<title>User info - SisAi world</title>
<meta name="description" id="site_desc" content="" />
<!-- Favicon -->
<link rel="icon" type="image/x-icon" href="files/favicon.ico" />
<!-- Fonts -->
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Public+Sans:ital,wght@0,300;0,400;0,500;0,600;0,700;1,300;1,400;1,500;1,600;1,700&display=swap"
rel="stylesheet"
/>
<!-- Icons. Uncomment required icon fonts -->
<link rel="stylesheet" href="files/boxicons.css?v=20230405" />
<!-- Core CSS -->
<link rel="stylesheet" href="files/core.css" class="template-customizer-core-css" />
<link rel="stylesheet" href="files/theme-default.css" class="template-customizer-theme-css" />
<link rel="stylesheet" href="files/demo.css" />
<!-- Vendors CSS -->
<link rel="stylesheet" href="files/perfect-scrollbar.css" />
<!-- Page CSS -->
<!-- Helpers -->
<script src="files/helpers.js"></script>
<!--! Template customizer & Theme config files MUST be included after core stylesheets and helpers.js in the <head> section -->
<!--? Config: Mandatory theme config file contain global vars & default theme options, Set your preferred theme option in this file. -->
<script src="files/config.js"></script>
<script src="files/jquery.js"></script>
<script src="files/popper.js"></script>
<script src="files/bootstrap.js"></script>
<script src="files/perfect-scrollbar.js"></script>
<script src="files/menu.js"></script>
<script src="files/main.js"></script>
<script src="files/ua-parser.js"></script>
<link rel="stylesheet" href="files/spinner.css" />
<script src="files/amipro_utils.js?v=20230401402"></script>
<script>
var user_id;
const i18n_messages = new Map();
var lang_map = new Map();
lang_map.set("en-US", "Add device");
lang_map.set("zh-CN", "添加设备");
lang_map.set("ja", "デバイスを追加");
i18n_messages.set("btn_add", lang_map);
lang_map = new Map();
lang_map.set("en-US", "Go to top page");
lang_map.set("zh-CN", "去首页");
lang_map.set("ja", "トップページへ");
i18n_messages.set("btn_top", lang_map);
lang_map = new Map();
lang_map.set("en-US", "My devices");
lang_map.set("zh-CN", "我的设备");
lang_map.set("ja", "マイデバイス");
i18n_messages.set("my_devices", lang_map);
lang_map = new Map();
lang_map.set("en-US", "Device registered");
lang_map.set("zh-CN", "添加设备成功");
lang_map.set("ja", "デバイス登録完了。");
i18n_messages.set("msg_register_ok", lang_map);
lang_map = new Map();
lang_map.set("en-US", "Device");
lang_map.set("zh-CN", "设备");
lang_map.set("ja", "デバイス");
i18n_messages.set("title_device", lang_map);
lang_map = new Map();
lang_map.set("en-US", "Registered time");
lang_map.set("zh-CN", "添加时间");
lang_map.set("ja", "登録時間");
i18n_messages.set("title_time", lang_map);
lang_map = new Map();
lang_map.set("en-US", "Actions");
lang_map.set("zh-CN", "操作");
lang_map.set("ja", "操作");
i18n_messages.set("title_act", lang_map);
lang_map = new Map();
lang_map.set("en-US", "Delete");
lang_map.set("zh-CN", "删除");
lang_map.set("ja", "削除");
i18n_messages.set("title_del", lang_map);
lang_map = new Map();
lang_map.set("en-US", "Contact us");
lang_map.set("zh-CN", "联系我们");
lang_map.set("ja", "お問い合わせ");
i18n_messages.set("title_contact", lang_map);
lang_map = new Map();
lang_map.set("en-US", "Log out");
lang_map.set("zh-CN", "登出");
lang_map.set("ja", "ログアウト");
i18n_messages.set("title_logout", lang_map);
lang_map = new Map();
lang_map.set("en-US", "Deleted device.");
lang_map.set("zh-CN", "设备删除成功。");
lang_map.set("ja", "デバイスを削除しました。");
i18n_messages.set("msg_deldev_ok", lang_map);
lang_map = new Map();
lang_map.set("en-US", "Do you want to delete this device?");
lang_map.set("zh-CN", "确认删除此设备吗?");
lang_map.set("ja", "デバイスを削除しますか?");
i18n_messages.set("msg_confirm_deldev", lang_map);
lang_map = new Map();
lang_map.set("en-US", "Account information");
lang_map.set("zh-CN", "账号信息");
lang_map.set("ja", "アカウント情報");
i18n_messages.set("user_info", lang_map);
lang_map = new Map();
lang_map.set("en-US", "Save");
lang_map.set("zh-CN", "保 存");
lang_map.set("ja", "保 存");
i18n_messages.set("btn_update_info", lang_map);
lang_map = new Map();
lang_map.set("en-US", "Nick name");
lang_map.set("zh-CN", "昵 称");
lang_map.set("ja", "ニックネーム");
i18n_messages.set("label_nick_name", lang_map);
lang_map = new Map();
lang_map.set("en-US", "Password");
lang_map.set("zh-CN", "密 码");
lang_map.set("ja", "パスワード");
i18n_messages.set("label_password", lang_map);
lang_map = new Map();
lang_map.set("en-US", "Password again");
lang_map.set("zh-CN", "确认密码");
lang_map.set("ja", "パスワード(確認)");
i18n_messages.set("label_pw_confirm", lang_map);
lang_map = new Map();
lang_map.set("en-US", "Password and confirmation password are different. Please re-enter.");
lang_map.set("zh-CN", "密码与确认密码不同,请重新输入。");
lang_map.set("ja", "パスワードが一致しません。もう一度入力してください。");
i18n_messages.set("msg_pw_confirm_err", lang_map);
lang_map = new Map();
lang_map.set("en-US", "Please enter a nickname. Enter between 5 and 30 characters.");
lang_map.set("zh-CN", "请输入昵称长度在5-30字之间。");
lang_map.set("ja", "ニックネームを入力してください。5-30文字の間で入力してください。");
i18n_messages.set("msg_input_nickname", lang_map);
lang_map = new Map();
lang_map.set("en-US", "This nickname is already in use. Please enter another nickname");
lang_map.set("zh-CN", "此昵称已经被使用,请重新输入。");
lang_map.set("ja", "このニックネームは既に使用されています。別のニックネームを入力してください。");
i18n_messages.set("msg_using_nickname", lang_map);
lang_map = new Map();
lang_map.set("en-US", "Password must be at least 8 characters long.");
lang_map.set("zh-CN", "请输入密码长度大于8个文字。");
lang_map.set("ja", "パスワードは8文字以上で入力してください。");
i18n_messages.set("msg_pw_short_err", lang_map);
lang_map = new Map();
lang_map.set("en-US", "System error. Please try later.");
lang_map.set("zh-CN", "系统错误,请稍后再试。");
lang_map.set("ja", "システムエラー。後でもう一度お試しください。");
i18n_messages.set("msg_sys_fail", lang_map);
var reg_session_id = null;
var invite_session_id = null;
window.onload = async function() {
setI18NText(i18n_messages);
let url = new URL(window.location.href);
let params = url.searchParams;
reg_session_id = params.get("rid");
invite_session_id = params.get("nid");
//showSpinner();
if(reg_session_id){
const reg_username = await getRegistrationUser(reg_session_id);
user_id = reg_username;
if(reg_username && 0 < reg_username.length){
$('#user_email').html(user_id);
}else{
window.location.href = "login.html";
}
}else if(invite_session_id){
const response = await fetch("/invtinguser", {
method: "POST",
cache: "no-cache",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({session: invite_session_id})
});
if(response.status == 200){
const resp = await response.json();
if (resp.status=='OK') {
if(resp.exist){
processInviteSession(invite_session_id)
window.location.href = "top.html";
}else{
//Register user first
$('#user_email').html(resp.user_email);
}
}else{//No session
alert(getI18NText(i18n_messages, 'msg_sys_fail'));
window.location.href = "login.html";
}
}
}
hideSpinner();
}
async function getRegistrationUser(reg_session_id){
showSpinner();
var lstJson = {};
lstJson["session"] = reg_session_id;
try{
const response = await fetch("/getreguser", {
method: "POST",
cache: "no-cache",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(lstJson)
})
hideSpinner();
if(response.status == 200){
const resp = await response.json();
if (resp.status=='OK') {
return resp.user
}
}
}catch(err){
alert(getI18NText(i18n_messages, 'msg_sys_fail'));
}finally{
hideSpinner();
}
return null;
}
async function updateUser(){
if($('#password').val().length < 8){
alert(getI18NText(i18n_messages, 'msg_pw_short_err'));
//alert("Password must be at least 8 characters long.");
return;
}
if($('#password').val() != $('#pw_confirm').val()){
alert(getI18NText(i18n_messages, 'msg_pw_confirm_err'));
return;
}
var user_name = $('#user_name').val().trim();
if(user_name.length < 5 || user_name.length > 30){
alert(getI18NText(i18n_messages, 'msg_input_nickname'));
return;
}
showSpinner();
//add try-catch for await rejection
try{
const result = await fetch("/chksamenm", {
method: "POST",
cache: "no-cache",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({type:'nick', nickname: user_name})
});
if(result.status == 200){
const resp = await result.json();
if (resp.status=='OK') {
if(resp.same){
hideSpinner();
alert(getI18NText(i18n_messages, 'msg_using_nickname'));
return;
}
}
}else{
hideSpinner();
return;
}
var lstJson = {};
if(reg_session_id && 0<reg_session_id.length) lstJson["reg_session_id"] = reg_session_id;
else if(invite_session_id && 0<invite_session_id.length) lstJson["invt_session_id"] = invite_session_id;
lstJson["user_name"] = user_name;
lstJson["password"] = $('#password').val();
lstJson["language"] = window.navigator.language;
const response = await fetch("/setuser", {
method: "POST",
cache: "no-cache",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(lstJson)
})
if(response.status == 200){
const email = $('#user_email').html();
const resp = await response.json();
if (resp.status=='OK') {
if(reg_session_id){
login(email, lstJson["password"],"market.html");
}else if(invite_session_id && 0 < invite_session_id.length){
processInviteSession(invite_session_id)
login(email, lstJson["password"], "top.html");
}else{
window.location.href = "top.html";
}
}else{
alert(resp.errorMessage);
}
}
}catch(err){
alert(getI18NText(i18n_messages, 'msg_sys_fail'));
}finally{
hideSpinner();
}
}
async function login(email, pwd, goto){
const response = await fetch("/login", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({email: email, pwd: pwd}),
cache: "no-cache"
})
if(response.status == 200){
const resp = await response.json();
if (resp.status!='OK') {
window.location.href = "top.html";
}else{
const nm = getI18NJsonText(resp.nickname);
sessionStorage.setItem('nickname', nm);
window.location.href = goto;
}
}
}
async function processInviteSession(invite_session_id){
showSpinner();
var lstJson = {};
lstJson["session"] = invite_session_id;
try{
const response = await fetch("/invt", {
method: "POST",
cache: "no-cache",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(lstJson)
})
hideSpinner();
if(response.status == 200){
const resp = await response.json();
if (resp.status=='OK') {
return resp.user_email
}
}
}catch(err){
alert(getI18NText(i18n_messages, 'msg_sys_fail'));
}finally{
hideSpinner();
}
return null;
}
async function registerUser(user_email) {
try {
let req = {user_email: user_email, session_id: sessionStorage.getItem("session_id")}
const response = await fetch("/reguser", {
method: "POST",
cache: "no-cache",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(req)
})
} catch (err) {
console.log(err)
return null;
}finally{
hideSpinner();
}
}
async function addDevice(){
const the_user_id = user_id?user_id:sessionStorage.getItem("uid")
const result = await registerFido2(the_user_id, 'user_'+the_user_id);
var rtn = false
if(result.status === 'ok'){
if(reg_session_id){
await registerUser(the_user_id);
}
alert($('#msg_register_ok').html());
rtn = true;
}else{
errProcessFido2(result)
rtn = false;
}
listDevices();
return rtn
}
async function delDevice(device_id){
if(window.confirm(getI18NText(i18n_messages, 'msg_confirm_deldev')) == true){
const result = await delUserDeviceFido2(device_id)
if("ok" === result.status){
alert(getI18NText(i18n_messages, 'msg_deldev_ok'));
listDevices();
}else{
const msg = getI18NErrorMessage(result.errorMessage);
alert(msg?msg:result.errorMessage);
}
}
}
async function listDevices(){
const result = await listUserDevicesFido2()
$("#devices_list").html("");
if("ok" === result.status){
let lst_html = ""
for(let dev of result.devices){
var dev_desc = dev.desc
if(!dev_desc || 0 === dev_desc.length){
let parser = new UAParser(dev.userAgent);
if(parser.getOS().name){
dev_desc = parser.getDevice().model + ',' + parser.getOS().name + ',' + parser.getBrowser().name;
}else{
dev_desc = dvc.userAgent;
}
}
var date = new Date(dev.registered_time);
lst_html += '<tr><td><strong>'+ dev_desc +'</strong></td><td>'+
date.toLocaleString()+'</td><td>'+
"<a style=\"padding-left: 0px;\" class=\"dropdown-item\" href=\"javascript:delDevice('"+dev.device_id+"');\">"+
'<i class="bx bx-trash me-1"></i><span id="title_del"> '+ getI18NText(i18n_messages, 'title_del') +'</span></a></div></td></tr>'
}
$("#devices_list").html(lst_html);
}else{
const msg = getI18NErrorMessage(result.errorMessage); //fido2LibErrMsgLanguages.japanese
alert(msg?msg:result.errorMessage);
}
}
function showSpinner() {
$('#btn_add').prop('disabled', true);
$('#btn_top').prop('disabled', true);
document.getElementById("spinner").style.display = "block";
}
function hideSpinner() {
$('#btn_add').prop('disabled', false);
$('#btn_top').prop('disabled', false);
document.getElementById("spinner").style.display = "none";
}
</script>
</head>
<body>
<div id="msg_register_ok" style="display:none;">Device registered</div>
<!-- Layout wrapper -->
<div class="layout-wrapper layout-content-navbar">
<div class="layout-container">
<!-- Layout container -->
<div class="layout-page">
<!-- Navbar -->
<nav
class="layout-navbar container-xxl navbar navbar-expand-xl navbar-detached align-items-center bg-navbar-theme"
id="layout-navbar"
>
<div class="layout-menu-toggle navbar-nav align-items-xl-center me-3 me-xl-0 d-xl-none">
<a class="nav-item nav-link px-0 me-xl-4" href="javascript:void(0)">
<i class="bx bx-menu bx-sm"></i>
</a>
</div>
<div class="navbar-nav-right d-flex align-items-center" id="navbar-collapse">
<div class="navbar-nav align-items-center">
<div class="nav-item d-flex align-items-center">
<i class="bx bx-user fs-4 lh-0"></i>
<span id="user_email"></span>
</div>
</div>
<ul class="navbar-nav flex-row align-items-center ms-auto">
<li>
<button style="display:none;" class="btn btn-info" onclick="javascript:window.location='top.html'"
id="btn_top">Go to top</button>
</li>
</ul>
</div>
</nav>
<!-- / Navbar -->
<!-- Content wrapper -->
<div class="content-wrapper">
<!-- Content -->
<div>
<div class="container-xxl flex-grow-1 container-p-y">
<div class="card">
<h5 class="card-header" id="user_info">My email</h5>
<div class="card-body">
<div class="row justify-content-center">
<div class="col-md-6">
<!-- form class="mb-3" action="javascript:authenticate();" method="POST"></!-->
<div class="mb-3">
<label for="uid" class="form-label" id="label_nick_name">Nick name</label>
<input type="text" class="form-control" id="user_name" name="user_name" placeholder="Enter your nick name" autofocus="">
</div>
<div class="mb-3 form-password-toggle">
<div class="d-flex justify-content-between">
<label class="form-label" for="password" id="label_password">Password</label>
</div>
<div class="input-group input-group-merge">
<input type="password" id="password" class="form-control" name="password" placeholder="············" >
<span class="input-group-text cursor-pointer"></span>
</div>
</div>
<div class="mb-3 form-password-toggle">
<div class="d-flex justify-content-between">
<label class="form-label" for="pw_confirm" id="label_pw_confirm">Password again</label>
</div>
<div class="input-group input-group-merge">
<input type="password" id="pw_confirm" class="form-control" name="pw_confirm" placeholder="············" >
<span class="input-group-text cursor-pointer"></i></span>
</div>
</div>
<div class="mb-3">
<button class="btn btn-primary d-grid w-100" id="btn_update_info" onclick="javascript:updateUser();">Update</button>
</div>
<!-- /form -->
</div>
</div>
</div>
</div>
</div>
<!-- div class="container-xxl flex-grow-1 container-p-y">
<div class="card">
<h5 class="card-header" id="my_devices">My devices</h5>
<div style="margin-right: 10px;">
<button class="btn btn-primary" style="width:30%;float: right;margin-left: 66%;" onclick="addDevice();"
id="btn_add">Add device</button>
</div>
<div class="table-responsive text-nowrap">
<table class="table">
<thead>
<tr>
<th id="title_device">Device</th>
<th id="title_time">Registered time</th>
<th id="title_act">Actions</th>
</tr>
</thead>
<tbody class="table-border-bottom-0" id="devices_list">
<tr>
<td colspan="3" style="text-align: center;" id="title_empty_list"></td>
</tr>
</tbody>
</table>
</div>
</div>
</ -->
<div id="spinner" class="spinner">
<div class="spinner-icon"></div>
</div>
<!-- / Content -->
<!-- Footer -->
<footer class="content-footer footer bg-footer-theme">
<div class="container-xxl d-flex flex-wrap justify-content-between py-2 flex-md-row flex-column">
<div class="mb-2 mb-md-0">
©
<script>
document.write(new Date().getFullYear());
</script>
<a href="#" target="_blank" class="footer-link fw-bolder">amiPro(Powered by OpenAI-4o)</a>
</div>
<div>
<a
href="mailto:support@amipro.me?subject=contact"
target="_blank"
class="footer-link me-4"
id="title_contact"
>Contact</a
>
</div>
</div>
</footer>
<!-- / Footer -->
<div class="content-backdrop fade"></div>
</div>
<!-- Content wrapper -->
</div>
<!-- / Layout page -->
</div>
<!-- Overlay -->
<div class="layout-overlay layout-menu-toggle"></div>
</div>
<!-- / Layout wrapper -->
<!-- div class="buy-now">
<a
href="https://themeselection.com/products/sneat-bootstrap-html-admin-template/"
target="_blank"
class="btn btn-danger btn-buy-now"
>Upgrade to Pro</a
>
</div -->
<!-- Page JS -->
<!-- Place this tag in your head or just before your close body tag. -->
<script async defer src="https://buttons.github.io/buttons.js"></script>
</body>
</html>