Init Gitea

This commit is contained in:
dqj
2025-10-06 21:31:13 +09:00
commit c879b8de12
60 changed files with 41314 additions and 0 deletions

675
views/user.html Normal file
View File

@@ -0,0 +1,675 @@
<!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>