原始提交

This commit is contained in:
2022-02-22 18:04:33 +08:00
commit 014dbe4982
32 changed files with 4057 additions and 0 deletions

86
assets/index.html Normal file
View File

@@ -0,0 +1,86 @@
<!--
~ Copyright (C) 2022. Gardel <sunxinao@hotmail.com> and contributors
~
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU Affero General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ (at your option) any later version.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU Affero General Public License for more details.
~
~ You should have received a copy of the GNU Affero General Public License
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>注册</title>
<link rel="stylesheet" href="https://cdn.bootcss.com/normalize/8.0.0/normalize.min.css">
<!-- <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700"> -->
<link href="https://cdn.bootcss.com/material-components-web/5.1.0/material-components-web.min.css" rel="stylesheet">
<script src="https://cdn.bootcss.com/material-components-web/5.1.0/material-components-web.min.js"></script>
<link rel="stylesheet" href="login.css">
</head>
<body >
<noscript>You need to enable JavaScript to run this app.</noscript>
<section class="header">
<h1>简陋注册页</h1>
</section>
<form id="reg-form" action="#">
<div class="mdc-text-field username">
<input type="email" class="mdc-text-field__input" id="username-input" name="username" required>
<label class="mdc-floating-label" for="username-input">邮箱</label>
<div class="mdc-line-ripple"></div>
</div>
<div class="mdc-text-field profileName">
<input type="text" class="mdc-text-field__input" id="profileName-input" name="profileName" required minlength="2">
<label class="mdc-floating-label" for="profileName-input">角色名</label>
<div class="mdc-line-ripple"></div>
</div>
<div class="mdc-text-field-helper-line profileName-helper">
<div class="mdc-text-field-helper-text" aria-hidden="true">字母,数字或下划线</div>
</div>
<div class="mdc-text-field password">
<input type="password" class="mdc-text-field__input" id="password-input" name="password" required minlength="6">
<label class="mdc-floating-label" for="password-input">密码</label>
<div class="mdc-line-ripple"></div>
</div>
<div class="mdc-text-field-helper-line password-helper">
<div class="mdc-text-field-helper-text" aria-hidden="true">警告: 暂无重置密码接口,请妥善保管密码</div>
</div>
<div class="button-container">
<button class="mdc-button mdc-button--raised login">
<div class="mdc-button__ripple"></div>
<span class="mdc-button__label">
已有账号登录
</span>
</button>
<button type="submit" class="mdc-button mdc-button--raised next">
<div class="mdc-button__ripple"></div>
<span class="mdc-button__label">
下一步
</span>
</button>
</div>
</form>
<div class="mdc-snackbar">
<div class="mdc-snackbar__surface">
<div class="mdc-snackbar__label"
role="status"
aria-live="polite">
注册失败
</div>
</div>
</div>
<script src="https://cdn.bootcss.com/jquery/3.5.0/jquery.min.js"></script>
<script src="login.js" async></script>
</body>
</html>

51
assets/login.css Normal file
View File

@@ -0,0 +1,51 @@
/*
* Copyright (C) 2022. Gardel <sunxinao@hotmail.com> and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
html, body {
height: 100%;
}
body {
font-family: 'Roboto';
margin: 0;
padding-top: 0.1px;
}
.header {
text-align: center;
}
.username,
.profileName,
.password,
.profileName-helper,
.password-helper {
display: block;
width: 300px;
margin: 20px auto;
}
.button-container {
display: flex;
justify-content: flex-end;
width: 300px;
margin: auto;
}
.button-container button {
margin: 3px;
}

129
assets/login.js Normal file
View File

@@ -0,0 +1,129 @@
/*
* Copyright (C) 2022. Gardel <sunxinao@hotmail.com> and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
const MDCSnackbar = mdc.snackbar.MDCSnackbar;
const MDCTextField = mdc.textField.MDCTextField;
const MDCRipple = mdc.ripple.MDCRipple;
const snackbar = new MDCSnackbar(document.querySelector(".mdc-snackbar"));
const username = new MDCTextField(document.querySelector(".username"));
const password = new MDCTextField(document.querySelector(".password"));
const profileName = new MDCTextField(document.querySelector(".profileName"));
new MDCRipple(document.querySelector(".next"));
snackbar.close();
var login = false;
$(".login").click(function (btn) {
login = true;
$(".profileName").hide();
$("#profileName-input").removeAttr("required");
$(".next").children(".mdc-button__label").text("登录");
$(this).hide();
});
$("#reg-form").submit(function (e) {
if (!login) {
$.ajax({
url: "/authserver/register",
type: "POST",
dataType: "JSON",
contentType: "application/json",
data: JSON.stringify({
username: username.value,
password: password.value,
profileName: profileName.value
}),
success: function (data) {
if (!data.id) {
if (data.errorMessage) snackbar.labelText = data.errorMessage;
snackbar.open();
} else {
login = true;
$(".profileName").hide();
$(".login").hide();
$(".next").children(".mdc-button__label").text("登录");
snackbar.timeoutMs = 10000;
snackbar.labelText = "注册成功uid:" + data.id;
snackbar.open();
localStorage.uuid = data.id;
}
},
error: function (e) {
let response = JSON.parse(e.responseText);
if (response.errorMessage === "profileName exist") {
snackbar.labelText = "注册失败: 角色名已存在";
} else if (response.errorMessage === "profileName duplicate") {
snackbar.labelText = "注册失败: 角色名与正版用户冲突";
} else {
snackbar.labelText = "注册失败: " + response.errorMessage;
}
snackbar.open();
}
});
} else {
$.ajax({
url: "/authserver/authenticate",
type: "POST",
dataType: "JSON",
contentType: "application/json",
data: JSON.stringify({
username: username.value,
password: password.value
}),
success: function (data) {
if (!data.accessToken) {
snackbar.labelText = "登录失败:";
if (data.errorMessage) snackbar.labelText += data.errorMessage;
snackbar.open();
} else {
snackbar.timeoutMs = 5000;
snackbar.labelText = "登录成功accessToken:" + data.accessToken;
snackbar.open();
localStorage.accessToken = data.accessToken;
localStorage.loginTime = new Date().getTime();
localStorage.profileName = data.selectedProfile.name;
if (data.selectedProfile) {
localStorage.profileName = data.selectedProfile.name;
localStorage.uuid = data.selectedProfile.id;
}
// localStorage.username = username.value;
// localStorage.password = password.value;
setTimeout(function () {
window.location = "user.html";
}, 3000);
}
},
error: function (e) {
let response = JSON.parse(e.responseText);
snackbar.labelText = "登录失败: " + response.errorMessage;
snackbar.open();
}
});
}
e.preventDefault();
});
$(document).ready(function () {
if (!localStorage.accessToken && localStorage.loginTime !== undefined &&
(new Date().getTime() - localStorage.loginTime) < 30 * 86400 * 1000) {
window.location = "user.html";
}
});

48
assets/user.css Normal file
View File

@@ -0,0 +1,48 @@
/*
* Copyright (C) 2022. Gardel <sunxinao@hotmail.com> and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
.header {
text-align: center;
}
.model,
.textureType,
.url,
.changeTo {
display: block;
width: 300px;
margin: 20px auto;
}
.file {
display: flex;
width: 300px;
margin: 20px auto;
}
.button-container {
display: flex;
justify-content: flex-end;
width: 300px;
margin: auto;
}
.button-container button {
margin: 3px;
}

154
assets/user.html Normal file
View File

@@ -0,0 +1,154 @@
<!--
~ Copyright (C) 2022. Gardel <sunxinao@hotmail.com> and contributors
~
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU Affero General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ (at your option) any later version.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU Affero General Public License for more details.
~
~ You should have received a copy of the GNU Affero General Public License
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>角色信息</title>
<link rel="stylesheet" href="https://cdn.bootcss.com/normalize/8.0.0/normalize.min.css">
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
<link href="https://cdn.bootcss.com/material-components-web/5.1.0/material-components-web.min.css" rel="stylesheet">
<script src="https://cdn.bootcss.com/material-components-web/5.1.0/material-components-web.min.js"></script>
<link rel="stylesheet" href="user.css">
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<section class="header">
<h1>简陋信息页</h1>
</section>
<section class="header">
<h3>上传材质</h3>
</section>
<form id="upload-form" action="#" enctype="multipart/form-data">
<div class="mdc-form-field textureType">
<label>材质类别: </label>
<div id="radio-skin" class="mdc-radio">
<input class="mdc-radio__native-control" type="radio" id="radio-3" name="type" value="skin" checked>
<div class="mdc-radio__background">
<div class="mdc-radio__outer-circle"></div>
<div class="mdc-radio__inner-circle"></div>
</div>
<div class="mdc-radio__ripple"></div>
</div>
<label for="radio-3">皮肤</label>
<div id="radio-cape" class="mdc-radio">
<input class="mdc-radio__native-control" type="radio" id="radio-4" name="type" value="cape">
<div class="mdc-radio__background">
<div class="mdc-radio__outer-circle"></div>
<div class="mdc-radio__inner-circle"></div>
</div>
<div class="mdc-radio__ripple"></div>
</div>
<label for="radio-3">披风</label>
</div>
<div class="mdc-form-field model">
<label>材质模型: </label>
<div id="radio-steve" class="mdc-radio">
<input class="mdc-radio__native-control" type="radio" id="radio-1" name="model" value="default" checked>
<div class="mdc-radio__background">
<div class="mdc-radio__outer-circle"></div>
<div class="mdc-radio__inner-circle"></div>
</div>
<div class="mdc-radio__ripple"></div>
</div>
<label for="radio-1">Steve</label>
<div id="radio-alex" class="mdc-radio">
<input class="mdc-radio__native-control" type="radio" id="radio-2" name="model" value="slim">
<div class="mdc-radio__background">
<div class="mdc-radio__outer-circle"></div>
<div class="mdc-radio__inner-circle"></div>
</div>
<div class="mdc-radio__ripple"></div>
</div>
<label for="radio-2">Alex</label>
</div>
<div class="mdc-text-field url">
<input type="url" class="mdc-text-field__input" id="url-input" name="url">
<label class="mdc-floating-label" for="url-input">材质url</label>
<div class="mdc-line-ripple"></div>
</div>
<label class="mdc-text-field mdc-text-field--outlined mdc-text-field--with-trailing-icon file">
<input type="file" style="display: none;" accept="image/*" class="mdc-text-field__input" id="file-input"
name="file" aria-label="Label">
<i class="material-icons mdc-text-field__icon mdc-text-field__icon--trailing" tabindex="0" role="button"
onclick="$('#file-input').val('').change();">delete</i>
<div class="mdc-notched-outline">
<div class="mdc-notched-outline__leading"></div>
<div class="mdc-notched-outline__notch">
<label id="file-path" for="file-input" class="mdc-floating-label">或者选择一个图片</label>
</div>
<div class="mdc-notched-outline__trailing"></div>
</div>
</label>
<div class="button-container">
<button type="submit" class="mdc-button mdc-button--raised upload">
<div class="mdc-button__ripple"></div>
<span class="mdc-button__label">
上传
</span>
</button>
<button type="button" id="delete-btn" class="mdc-button mdc-button--raised upload">
<div class="mdc-button__ripple"></div>
<span class="mdc-button__label">
重置为默认
</span>
</button>
</div>
</form>
<section class="header">
<h3>更改游戏标签</h3>
</section>
<form id="change-form" action="#">
<div class="mdc-text-field changeTo">
<input type="text" class="mdc-text-field__input" id="changeTo-input" name="changeTo" required minlength="2"
maxlength="16">
<script>
if (localStorage.profileName) document.getElementById("changeTo-input").value = localStorage.profileName;
</script>
<label class="mdc-floating-label" for="changeTo-input">游戏标签</label>
<div class="mdc-line-ripple"></div>
</div>
<div class="button-container">
<button type="submit" class="mdc-button mdc-button--raised submit">
<div class="mdc-button__ripple"></div>
<span class="mdc-button__label">
更改
</span>
</button>
</div>
</form>
<div class="mdc-snackbar">
<div class="mdc-snackbar__surface">
<div class="mdc-snackbar__label"
role="status"
aria-live="polite">
上传失败
</div>
</div>
</div>
<script src="https://cdn.bootcss.com/jquery/3.5.0/jquery.min.js"></script>
<script src="user.js" async></script>
</body>
</html>

242
assets/user.js Normal file
View File

@@ -0,0 +1,242 @@
/*
* Copyright (C) 2022. Gardel <sunxinao@hotmail.com> and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
const MDCSnackbar = mdc.snackbar.MDCSnackbar;
const MDCTextField = mdc.textField.MDCTextField;
const MDCFormField = mdc.formField.MDCFormField;
const MDCRadio = mdc.radio.MDCRadio;
//const MDCRipple = mdc.ripple.MDCRipple;
const snackbar = new MDCSnackbar(document.querySelector('.mdc-snackbar'));
const modelField = new MDCFormField(document.querySelector('.model'));
const radio1 = new MDCRadio(document.querySelector('#radio-steve'));
const radio2 = new MDCRadio(document.querySelector('#radio-alex'));
const radio3 = new MDCRadio(document.querySelector('#radio-skin'));
const radio4 = new MDCRadio(document.querySelector('#radio-cape'));
modelField.input = {radio1, radio2};
const url = new MDCTextField(document.querySelector('.url'));
const file = new MDCTextField(document.querySelector('.file'));
const changeTo = new MDCTextField(document.querySelector('.changeTo'));
const modelTypeForm = document.querySelector('.mdc-form-field.model');
snackbar.close()
$("#file-input").change(function() {
const path = this.value;
if (!path) {
$("#file-path").text("或选择一张图片");
$(".url").show();
} else {
$("#file-path").text(path);
url.value = '';
$(".url").hide();
}
});
const modelTypeChange = function () {
if (radio3.checked) {
$(modelTypeForm).show();
} else {
$(modelTypeForm).hide();
}
}
$("#radio-3").change(modelTypeChange)
$("#radio-4").change(modelTypeChange)
$("#url-input").on("input", function() {
const url = this.value;
if (!url) {
$(".file").show();
} else {
file.value = '';
$(".file").hide();
}
});
$(document).ready(function() {
if (!localStorage.accessToken) {
localStorage.loginTime = 1;
window.location = "index.html";
}
$.ajax({
url: '/authserver/validate',
type: 'POST',
dataType: "JSON",
contentType: "application/json",
data: JSON.stringify({
accessToken: localStorage.accessToken,
}),
success: function(data) {
//有效,啥也不整
},
error: function(e) {
if (e.status == 403) {
// 持续套娃
$.ajax({
url: '/authserver/refresh',
type: 'POST',
dataType: "JSON",
contentType: "application/json",
data: JSON.stringify({
accessToken: localStorage.accessToken,
}),
success: function(data) {
if (!data.accessToken) {
localStorage.loginTime = 1;
window.location = "index.html";
} else {
snackbar.timeoutMs = 5000;
snackbar.labelText = "刷新token成功accessToken:" + data.accessToken;
snackbar.open();
localStorage.accessToken = data.accessToken;
localStorage.loginTime = new Date().getTime();
if(data.selectedProfile) {
localStorage.profileName = data.selectedProfile.name;
localStorage.uuid = data.selectedProfile.id;
}
}
},
error: function(e) {
if (e.status == 403) {
localStorage.loginTime = 1;
window.location = "index.html";
}
}
});
}
}
});
});
$("#upload-form").submit(function(e) {
e.preventDefault();
if (!url.value && !$("#file-input").val()) {
snackbar.timeoutMs = 5000;
snackbar.labelText = "没填信息";
snackbar.open();
return;
}
let textureType = 'skin'
if (radio3.checked) {
textureType = 'skin'
} else if (radio4.checked) {
textureType = 'cape'
}
if (!url.value) {
const formData = new FormData();
formData.append("model", radio1.checked ? radio1.value : radio2.value);
formData.append("file", $("#file-input")[0].files[0]);
//formData.contentType = "multipart/form-data";
$.ajax({
url: `/api/user/profile/${localStorage.uuid}/${textureType}`,
type: 'PUT',
processData: false,
contentType: false,
headers: {'Authorization':'Bearer ' + localStorage.accessToken},
data: formData,
success: function(data) {
snackbar.timeoutMs = 5000;
snackbar.labelText = "材质上传成功";
snackbar.open();
},
error: function(e) {
snackbar.timeoutMs = 5000;
snackbar.labelText = "材质上传失败";
snackbar.open();
}
});
} else if (url.value) {
$.ajax({
url: `/api/user/profile/${localStorage.uuid}/${textureType}`,
type: 'POST',
dataType: "JSON",
contentType: "application/json",
headers: {'Authorization':'Bearer ' + localStorage.accessToken},
data: JSON.stringify({
model: radio1.checked ? radio1.value : radio2.value,
url: url.value
}),
success: function(data) {
snackbar.timeoutMs = 5000;
snackbar.labelText = "材质上传成功";
snackbar.open();
},
error: function(e) {
snackbar.timeoutMs = 5000;
snackbar.labelText = "材质上传失败";
snackbar.open();
}
});
}
});
$("#change-form").submit(function(e) {
e.preventDefault();
if (changeTo.value.length <= 1) {
snackbar.timeoutMs = 5000;
snackbar.labelText = "更改失败, 角色名格式不正确";
snackbar.open();
return;
}
$.ajax({
url: '/authserver/change',
type: 'POST',
dataType: "JSON",
contentType: "application/json",
data: JSON.stringify({
accessToken: localStorage.accessToken,
changeTo: changeTo.value
}),
success: function(data) {
snackbar.timeoutMs = 5000;
snackbar.labelText = "更改成功";
snackbar.open();
localStorage.profileName = changeTo.value;
},
error: function(e) {
snackbar.timeoutMs = 5000;
snackbar.labelText = "更改失败, 可能是角色名已存在";
snackbar.open();
}
});
});
$('#delete-btn').click(function () {
let textureType = 'skin'
if (radio3.checked) {
textureType = 'skin'
} else if (radio4.checked) {
textureType = 'cape'
}
$.ajax({
url: `/api/user/profile/${localStorage.uuid}/${textureType}`,
type: 'DELETE',
headers: {'Authorization':'Bearer ' + localStorage.accessToken},
success: function(data) {
snackbar.timeoutMs = 5000;
snackbar.labelText = "恢复成功";
snackbar.open();
localStorage.profileName = changeTo.value;
},
error: function(e) {
snackbar.timeoutMs = 5000;
snackbar.labelText = "重置失败";
snackbar.open();
}
});
})