implement bots
This commit is contained in:
parent
67ff874dd3
commit
8cbbc1f1ff
3 changed files with 272 additions and 25 deletions
|
@ -17,7 +17,12 @@
|
|||
</button>
|
||||
<div class="collapse navbar-collapse" id="navbarNav" style="display:none!important">
|
||||
<ul class="navbar-nav me-auto">
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link active" href="#" id="usersNavLink">Users</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="#" id="botsNavLink">Bots</a>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="navbar-text dropdown">
|
||||
<a class="nav-link dropdown-toggle" href="#" id="accountDropdownMenuLink" role="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
|
@ -79,6 +84,7 @@
|
|||
<th>Date of Birth</th>
|
||||
<th>Created At</th>
|
||||
<th>Registration IP</th>
|
||||
<th>Developer?</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="usersTableBody"></tbody>
|
||||
|
@ -102,6 +108,36 @@
|
|||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="container-lg" id="botsView">
|
||||
<h1>Bots</h1>
|
||||
<form id="searchBotsForm">
|
||||
<div class="input-group">
|
||||
<span class="input-group-text">Owner</span>
|
||||
<input type="text" class="form-control" id="searchBotsOwner" name="owner"/>
|
||||
<button type="submit" class="btn btn-primary">Search</button>
|
||||
</div>
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>Username</th>
|
||||
<th>Rank</th>
|
||||
<th>Owner</th>
|
||||
<th>Created At</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="botsTableBody"></tbody>
|
||||
</table>
|
||||
</form>
|
||||
<h2>Create Bot</h2>
|
||||
<form id="createBotForm">
|
||||
<div class="input-group">
|
||||
<span class="input-group-text">Username</span>
|
||||
<input type="text" class="form-control" id="createBotUsername" name="username" required maxlength="20"/>
|
||||
<button type="submit" class="btn btn-primary">Create</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<script src="https://js.hcaptcha.com/1/api.js"></script>
|
||||
<script type="module" src="../ts/main.ts" type="application/javascript"></script>
|
||||
|
|
|
@ -37,7 +37,8 @@ export default class AuthManager {
|
|||
this.account = {
|
||||
username: json.username!,
|
||||
email: json.email!,
|
||||
sessionToken: json.token!
|
||||
sessionToken: json.token!,
|
||||
rank: json.rank!
|
||||
}
|
||||
}
|
||||
res(json);
|
||||
|
@ -61,6 +62,7 @@ export default class AuthManager {
|
|||
sessionToken: token,
|
||||
username: json.username!,
|
||||
email: json.email!,
|
||||
rank: json.rank!
|
||||
};
|
||||
}
|
||||
res(json);
|
||||
|
@ -107,10 +109,67 @@ export default class AuthManager {
|
|||
});
|
||||
}
|
||||
|
||||
updateRank(username : string, newRank : number) {
|
||||
return new Promise<UpdateRankResult>(async res => {
|
||||
updateUser(username : string, newRank : number | undefined = undefined, developer : boolean | undefined = undefined) {
|
||||
return new Promise<UpdateUserResult>(async res => {
|
||||
if (!this.account) throw new Error("Cannot update rank without logging in first");
|
||||
var data = await fetch(this.apiEndpoint + "/api/v1/admin/rank", {
|
||||
var data = await fetch(this.apiEndpoint + "/api/v1/admin/updateuser", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json"
|
||||
},
|
||||
body: JSON.stringify({
|
||||
token: this.account.sessionToken,
|
||||
username: username,
|
||||
rank: newRank,
|
||||
developer: developer,
|
||||
})
|
||||
});
|
||||
var json = await data.json() as UpdateUserResult;
|
||||
res(json);
|
||||
});
|
||||
}
|
||||
|
||||
listBots(resultsPerPage : number, page : number, owner : string | undefined) {
|
||||
return new Promise<ListBotsResult>(async res => {
|
||||
var data = await fetch(this.apiEndpoint + "/api/v1/bots/list", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json"
|
||||
},
|
||||
body: JSON.stringify({
|
||||
token: this.account!.sessionToken,
|
||||
resultsPerPage: resultsPerPage,
|
||||
page: page,
|
||||
owner: owner
|
||||
})
|
||||
});
|
||||
var json = await data.json() as ListBotsResult;
|
||||
res(json);
|
||||
});
|
||||
}
|
||||
|
||||
createBot(username : string) {
|
||||
return new Promise<CreateBotResult>(async res => {
|
||||
if (!this.account) throw new Error("Cannot create bot without logging in first");
|
||||
var data = await fetch(this.apiEndpoint + "/api/v1/bots/create", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json"
|
||||
},
|
||||
body: JSON.stringify({
|
||||
token: this.account.sessionToken,
|
||||
username: username
|
||||
})
|
||||
});
|
||||
var json = await data.json() as CreateBotResult;
|
||||
res(json);
|
||||
});
|
||||
}
|
||||
|
||||
adminUpdateBot(username : string, newRank : number | undefined) {
|
||||
return new Promise<AdminUpdateBotResult>(async res => {
|
||||
if (!this.account) throw new Error("Cannot update bot without logging in first");
|
||||
var data = await fetch(this.apiEndpoint + "/api/v1/admin/updatebot", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json"
|
||||
|
@ -121,7 +180,7 @@ export default class AuthManager {
|
|||
rank: newRank
|
||||
})
|
||||
});
|
||||
var json = await data.json() as UpdateRankResult;
|
||||
var json = await data.json() as AdminUpdateBotResult;
|
||||
res(json);
|
||||
});
|
||||
}
|
||||
|
@ -163,6 +222,7 @@ export interface Account {
|
|||
username : string;
|
||||
email : string;
|
||||
sessionToken : string;
|
||||
rank : number;
|
||||
}
|
||||
|
||||
export interface User {
|
||||
|
@ -174,6 +234,7 @@ export interface User {
|
|||
dateOfBirth : string;
|
||||
dateJoined : string;
|
||||
registrationIp : string;
|
||||
developer : boolean;
|
||||
}
|
||||
|
||||
export interface ListUsersResult {
|
||||
|
@ -183,7 +244,33 @@ export interface ListUsersResult {
|
|||
users : User[] | undefined;
|
||||
}
|
||||
|
||||
export interface UpdateRankResult {
|
||||
export interface UpdateUserResult {
|
||||
success : boolean;
|
||||
error : string | undefined;
|
||||
}
|
||||
|
||||
export interface Bot {
|
||||
id : number;
|
||||
username : string;
|
||||
rank : number;
|
||||
owner : string;
|
||||
created : string;
|
||||
}
|
||||
|
||||
export interface ListBotsResult {
|
||||
success : boolean;
|
||||
error : string | undefined;
|
||||
totalPageCount : number | undefined;
|
||||
bots : Bot[] | undefined;
|
||||
}
|
||||
|
||||
export interface CreateBotResult {
|
||||
success : boolean;
|
||||
error : string | undefined;
|
||||
token : string | undefined;
|
||||
}
|
||||
|
||||
export interface AdminUpdateBotResult {
|
||||
success : boolean;
|
||||
error : string | undefined;
|
||||
}
|
160
src/ts/main.ts
160
src/ts/main.ts
|
@ -26,6 +26,19 @@ const elements = {
|
|||
usersPerPage: document.getElementById('usersPerPage') as HTMLInputElement,
|
||||
usersPageCount: document.getElementById('usersPageCount') as HTMLSpanElement,
|
||||
usersTableBody: document.getElementById('usersTableBody') as HTMLTableSectionElement,
|
||||
|
||||
usersNavLink: document.getElementById('usersNavLink') as HTMLAnchorElement,
|
||||
botsNavLink: document.getElementById('botsNavLink') as HTMLAnchorElement,
|
||||
|
||||
usersView: document.getElementById('usersView') as HTMLDivElement,
|
||||
botsView: document.getElementById('botsView') as HTMLDivElement,
|
||||
|
||||
searchBotsForm: document.getElementById('searchBotsForm') as HTMLFormElement,
|
||||
searchBotsOwner: document.getElementById('searchBotsOwner') as HTMLInputElement,
|
||||
botsTableBody: document.getElementById('botsTableBody') as HTMLTableSectionElement,
|
||||
|
||||
createBotForm: document.getElementById('createBotForm') as HTMLFormElement,
|
||||
createBotUsername: document.getElementById('createBotUsername') as HTMLInputElement,
|
||||
};
|
||||
|
||||
const RankString = {
|
||||
|
@ -89,18 +102,19 @@ elements.searchUsersForm.addEventListener('submit', async e => {
|
|||
elements.usersPageCount.innerText = data.totalPageCount!.toString(10);
|
||||
elements.usersPage.max = data.totalPageCount!.toString(10);
|
||||
for (var user of data.users!) {
|
||||
var row = elements.usersTableBody.insertRow();
|
||||
var cell = row.insertCell();
|
||||
cell.innerText = user.id.toString(10);
|
||||
cell = row.insertCell();
|
||||
cell.innerText = user.username;
|
||||
cell = row.insertCell();
|
||||
cell.innerText = user.email;
|
||||
cell = row.insertCell();
|
||||
// Rank dropdown
|
||||
(() => {
|
||||
var _user = user;
|
||||
var row = elements.usersTableBody.insertRow();
|
||||
var cell = row.insertCell();
|
||||
cell.innerText = user.id.toString(10);
|
||||
cell = row.insertCell();
|
||||
cell.innerText = user.username;
|
||||
cell = row.insertCell();
|
||||
cell.innerText = user.email;
|
||||
cell = row.insertCell();
|
||||
// Rank dropdown
|
||||
var rankSelect = document.createElement('select');
|
||||
rankSelect.classList.add('form-select');
|
||||
rankSelect.innerHTML = `<option value="1">User</option><option value="2">Administrator</option><option value="3">Moderator</option>`;
|
||||
rankSelect.value = _user.rank.toString(10);
|
||||
rankSelect.addEventListener('change', async e => {
|
||||
|
@ -111,25 +125,106 @@ elements.searchUsersForm.addEventListener('submit', async e => {
|
|||
rankSelect.value = _user.rank.toString(10);
|
||||
return false;
|
||||
}
|
||||
var result = await auth.updateRank(_user.username, newRank);
|
||||
var result = await auth.updateUser(_user.username, newRank);
|
||||
if (!result.success) {
|
||||
alert("Failed to set rank: " + result.error);
|
||||
}
|
||||
});
|
||||
cell.appendChild(rankSelect);
|
||||
cell = row.insertCell();
|
||||
cell.innerText = user.banned ? "Yes" : "No";
|
||||
cell = row.insertCell();
|
||||
cell.innerText = user.dateOfBirth;
|
||||
cell = row.insertCell();
|
||||
cell.innerText = user.dateJoined;
|
||||
cell = row.insertCell();
|
||||
cell.innerText = user.registrationIp;
|
||||
cell = row.insertCell();
|
||||
var developerCheckbox = document.createElement('input');
|
||||
developerCheckbox.type = 'checkbox';
|
||||
developerCheckbox.checked = _user.developer;
|
||||
developerCheckbox.addEventListener('change', async e => {
|
||||
var developer = developerCheckbox.checked;
|
||||
// @ts-ignore
|
||||
if (!window.confirm(`Are you sure you want to ${developer ? "grant" : "revoke"} developer status ${developer ? "to" : "from"} ${_user.username}?`)) {
|
||||
e.preventDefault();
|
||||
developerCheckbox.checked = !developer;
|
||||
return false;
|
||||
}
|
||||
var result = await auth.updateUser(_user.username, undefined, developer);
|
||||
if (!result.success) {
|
||||
alert("Failed to update developer status: " + result.error);
|
||||
}
|
||||
});
|
||||
cell.appendChild(developerCheckbox);
|
||||
})();
|
||||
cell = row.insertCell();
|
||||
cell.innerText = user.banned ? "Yes" : "No";
|
||||
cell = row.insertCell();
|
||||
cell.innerText = user.dateOfBirth;
|
||||
cell = row.insertCell();
|
||||
cell.innerText = user.dateJoined;
|
||||
cell = row.insertCell();
|
||||
cell.innerText = user.registrationIp;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
elements.searchBotsForm.addEventListener('submit', async e => {
|
||||
e.preventDefault();
|
||||
var owner = elements.searchBotsOwner.value;
|
||||
var data = await auth.listBots(10, 1, owner === "" ? undefined : owner);
|
||||
if (!data.success) {
|
||||
alert("Failed to list bots: " + data.error);
|
||||
return false;
|
||||
}
|
||||
elements.botsTableBody.innerHTML = "";
|
||||
for (const bot of data.bots!) {
|
||||
(()=>{
|
||||
var row = elements.botsTableBody.insertRow();
|
||||
var cell = row.insertCell();
|
||||
cell.innerText = bot.id.toString(10);
|
||||
cell = row.insertCell();
|
||||
cell.innerText = bot.username;
|
||||
cell = row.insertCell();
|
||||
if (auth!.account!.rank === 2) {
|
||||
var rankSelect = document.createElement('select');
|
||||
rankSelect.classList.add('form-select');
|
||||
rankSelect.innerHTML = `<option value="1">User</option><option value="2">Administrator</option><option value="3">Moderator</option>`;
|
||||
rankSelect.value = bot.rank.toString(10);
|
||||
rankSelect.addEventListener('change', async e => {
|
||||
var newRank = parseInt(rankSelect.value);
|
||||
// @ts-ignore
|
||||
if (!window.confirm(`Are you sure you want to set ${bot.username}'s rank to ${RankString[newRank]}?`)) {
|
||||
e.preventDefault();
|
||||
rankSelect.value = bot.rank.toString(10);
|
||||
return false;
|
||||
}
|
||||
var result = await auth.adminUpdateBot(bot.username, newRank);
|
||||
if (!result.success) {
|
||||
alert("Failed to set rank: " + result.error);
|
||||
}
|
||||
});
|
||||
cell.appendChild(rankSelect);
|
||||
} else {
|
||||
// @ts-ignore
|
||||
cell.innerText = RankString[bot.rank];
|
||||
}
|
||||
cell = row.insertCell();
|
||||
cell.innerText = bot.owner;
|
||||
cell = row.insertCell();
|
||||
cell.innerText = bot.created;
|
||||
})();
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
elements.createBotForm.addEventListener('submit', async e => {
|
||||
e.preventDefault();
|
||||
var username = elements.createBotUsername.value;
|
||||
var result = await auth.createBot(username);
|
||||
if (!result.success) {
|
||||
alert("Failed to create bot: " + result.error);
|
||||
} else {
|
||||
alert(`Bot created successfully! Your bot's token is:\n\n${result.token}\n\nPlease save this token as it will not be shown again.`);
|
||||
}
|
||||
elements.createBotUsername.value = "";
|
||||
elements.searchBotsForm.requestSubmit();
|
||||
return false;
|
||||
});
|
||||
|
||||
(async () => {
|
||||
await auth.getAPIInformation();
|
||||
if (auth!.info!.hcaptcha.required) {
|
||||
|
@ -146,6 +241,7 @@ elements.searchUsersForm.addEventListener('submit', async e => {
|
|||
localStorage.removeItem(`collabvm_session_${new URL(Config.APIEndpoint).host}`);
|
||||
loadLoginForm();
|
||||
}
|
||||
loadUsersView();
|
||||
loadAdminView();
|
||||
} else {
|
||||
localStorage.removeItem(`collabvm_session_${new URL(Config.APIEndpoint).host}`);
|
||||
|
@ -154,6 +250,26 @@ elements.searchUsersForm.addEventListener('submit', async e => {
|
|||
} else loadLoginForm();
|
||||
})();
|
||||
|
||||
// nav link event listeners
|
||||
elements.usersNavLink.addEventListener('click', e => {
|
||||
e.preventDefault();
|
||||
loadUsersView();
|
||||
return false;
|
||||
});
|
||||
elements.botsNavLink.addEventListener('click', e => {
|
||||
e.preventDefault();
|
||||
loadBotsView();
|
||||
return false;
|
||||
});
|
||||
|
||||
function loadBotsView() {
|
||||
elements.searchBotsForm.requestSubmit();
|
||||
elements.botsNavLink.classList.add('active');
|
||||
elements.usersNavLink.classList.remove('active');
|
||||
elements.usersView.style.display = "none";
|
||||
elements.botsView.style.display = "block";
|
||||
}
|
||||
|
||||
function loadAdminView() {
|
||||
elements.loginView.style.display = "none";
|
||||
elements.adminView.style.display = "block";
|
||||
|
@ -167,4 +283,12 @@ function loadLoginForm() {
|
|||
elements.navbarNav.setAttribute("style", "display:none!important");
|
||||
elements.loadingText.style.display = "none";
|
||||
elements.adminLoginForm.style.display = "block";
|
||||
}
|
||||
|
||||
function loadUsersView() {
|
||||
elements.searchUsersForm.requestSubmit();
|
||||
elements.usersNavLink.classList.add('active');
|
||||
elements.botsNavLink.classList.remove('active');
|
||||
elements.usersView.style.display = "block";
|
||||
elements.botsView.style.display = "none";
|
||||
}
|
Loading…
Reference in a new issue