admin updates + client update
This commit is contained in:
parent
ee7806da74
commit
a798f41ca6
20 changed files with 299 additions and 70 deletions
|
@ -1,7 +1,7 @@
|
||||||
// ==UserScript==
|
// ==UserScript==
|
||||||
// @name MultiProbe
|
// @name MultiProbe
|
||||||
// @namespace https://*.angusnicneven.com/*
|
// @namespace https://*.angusnicneven.com/*
|
||||||
// @version 20241009.0
|
// @version 20241012.0
|
||||||
// @description Probe with friends!
|
// @description Probe with friends!
|
||||||
// @author tgpholly
|
// @author tgpholly
|
||||||
// @match https://*.angusnicneven.com/*
|
// @match https://*.angusnicneven.com/*
|
||||||
|
@ -55,7 +55,7 @@ console.log("[MP] MultiProbe init");
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
// Make sure to change the userscript version too!!!!!!!!!!
|
// Make sure to change the userscript version too!!!!!!!!!!
|
||||||
const USERSCRIPT_VERSION_RAW = "20241009.0";
|
const USERSCRIPT_VERSION_RAW = "20241012.0";
|
||||||
const USERSCRIPT_VERSION = parseInt(USERSCRIPT_VERSION_RAW.replace(".", ""));
|
const USERSCRIPT_VERSION = parseInt(USERSCRIPT_VERSION_RAW.replace(".", ""));
|
||||||
|
|
||||||
if (!continueRunningScript) {
|
if (!continueRunningScript) {
|
||||||
|
@ -799,6 +799,7 @@ mp_button {
|
||||||
.writeShortString(apiKey)
|
.writeShortString(apiKey)
|
||||||
.writeString(currentPage)
|
.writeString(currentPage)
|
||||||
.toBuffer());
|
.toBuffer());
|
||||||
|
pingStartTime = performance.now();
|
||||||
keepAliveInterval = setInterval(() => {
|
keepAliveInterval = setInterval(() => {
|
||||||
pingStartTime = performance.now();
|
pingStartTime = performance.now();
|
||||||
ws.send(keepAlivePacket);
|
ws.send(keepAlivePacket);
|
||||||
|
|
|
@ -6,23 +6,28 @@ import AdminExpireSessionModel from "../models/admin/AdminExpireSessionModel";
|
||||||
import AdminIndexViewModel from "../models/admin/AdminIndexViewModel";
|
import AdminIndexViewModel from "../models/admin/AdminIndexViewModel";
|
||||||
import AdminPartiesViewModel from "../models/admin/AdminPartiesViewModel";
|
import AdminPartiesViewModel from "../models/admin/AdminPartiesViewModel";
|
||||||
import AdminPartyViewModel from "../models/admin/AdminPartyViewModel";
|
import AdminPartyViewModel from "../models/admin/AdminPartyViewModel";
|
||||||
|
import AdminUserBadgesViewModel from "../models/admin/AdminUserBadgesViewModel";
|
||||||
import AdminUsersViewModel from "../models/admin/AdminUsersViewModel";
|
import AdminUsersViewModel from "../models/admin/AdminUsersViewModel";
|
||||||
import AdminUserViewModel from "../models/admin/AdminUserViewModel";
|
import AdminUserViewModel from "../models/admin/AdminUserViewModel";
|
||||||
import AdminWebSessionsViewModel from "../models/admin/AdminWebSessionsViewModel";
|
import AdminWebSessionsViewModel from "../models/admin/AdminWebSessionsViewModel";
|
||||||
import AdminWsSessionsViewModel from "../models/admin/AdminWsSessionsViewModel";
|
import AdminWsSessionsViewModel from "../models/admin/AdminWsSessionsViewModel";
|
||||||
|
import FunkyArray from "funky-array";
|
||||||
import Session from "../objects/Session";
|
import Session from "../objects/Session";
|
||||||
import WsData from "../objects/WsData";
|
import WsData from "../objects/WsData";
|
||||||
import BadgeService from "../services/BadgeService";
|
import BadgeService from "../services/BadgeService";
|
||||||
import PartyService from "../services/PartyService";
|
import PartyService from "../services/PartyService";
|
||||||
import UserService from "../services/UserService";
|
import UserService from "../services/UserService";
|
||||||
import Controller from "./Controller";
|
import Controller from "./Controller";
|
||||||
|
import UserBadgeListItem from "../entities/list/UserBadgeListItem";
|
||||||
|
import AdminRemoveUserBadgeModel from "../models/admin/AdminRemoveUserBadgeModel";
|
||||||
|
|
||||||
export default class AdminController_Auth$Admin extends Controller {
|
export default class AdminController_Auth$Admin extends Controller {
|
||||||
public async Index_Get() {
|
public async Index_Get() {
|
||||||
const adminIndexViewModel: AdminIndexViewModel = {
|
const adminIndexViewModel: AdminIndexViewModel = {
|
||||||
userCount: await UserService.GetUserCount(),
|
userCount: await UserService.GetUserCount(),
|
||||||
partyCount: await PartyService.GetPartyCount(),
|
partyCount: await PartyService.GetPartyCount(),
|
||||||
badgeCount: await BadgeService.GetBadgeCount()
|
badgeCount: await BadgeService.GetBadgeCount(),
|
||||||
|
userBadgeCount: await BadgeService.GetUnlockedBadgeCount()
|
||||||
};
|
};
|
||||||
|
|
||||||
return this.view(adminIndexViewModel);
|
return this.view(adminIndexViewModel);
|
||||||
|
@ -178,4 +183,34 @@ export default class AdminController_Auth$Admin extends Controller {
|
||||||
|
|
||||||
return this.view(adminWsSessionsViewModel);
|
return this.view(adminWsSessionsViewModel);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async UserBadges_Get(adminUserBadgesViewModel: AdminUserBadgesViewModel) {
|
||||||
|
const unlockedBadgeListItems = await BadgeService.GetUnlockedBadgeList(adminUserBadgesViewModel.badgeq, adminUserBadgesViewModel.userq);
|
||||||
|
|
||||||
|
const unlockedBadgeByBadgeId = new FunkyArray<number, Array<UserBadgeListItem>>();
|
||||||
|
let unlockedBadgeByBadgeIdArray: Array<UserBadgeListItem> | undefined;
|
||||||
|
for (const unlockedBadgeListItem of unlockedBadgeListItems) {
|
||||||
|
if (!(unlockedBadgeByBadgeIdArray = unlockedBadgeByBadgeId.get(unlockedBadgeListItem.BadgeId))) {
|
||||||
|
unlockedBadgeByBadgeId.set(unlockedBadgeListItem.BadgeId, unlockedBadgeByBadgeIdArray = new Array<UserBadgeListItem>());
|
||||||
|
}
|
||||||
|
|
||||||
|
unlockedBadgeByBadgeIdArray.push(unlockedBadgeListItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
adminUserBadgesViewModel.badges = await BadgeService.LoadList(adminUserBadgesViewModel.badgeq);
|
||||||
|
adminUserBadgesViewModel.unlockByBadgeId = unlockedBadgeByBadgeId
|
||||||
|
|
||||||
|
return this.view(adminUserBadgesViewModel);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async RemoveUserBadge_Get(adminRemoveUserBadgeModel: AdminRemoveUserBadgeModel) {
|
||||||
|
if (typeof(adminRemoveUserBadgeModel.id) === "undefined" || typeof(adminRemoveUserBadgeModel.id) !== "string") {
|
||||||
|
return this.badRequest();
|
||||||
|
}
|
||||||
|
const userBadgeId = parseInt(adminRemoveUserBadgeModel.id);
|
||||||
|
|
||||||
|
UserService.DeleteUnlockedBadge(this.session.userId, userBadgeId);
|
||||||
|
|
||||||
|
return this.redirectToAction("userbadges");
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -10,19 +10,7 @@ export default class Party {
|
||||||
public DeletedDatetime?:Date;
|
public DeletedDatetime?:Date;
|
||||||
public IsDeleted:boolean;
|
public IsDeleted:boolean;
|
||||||
|
|
||||||
public constructor(id?:number, partyRef?:string, name?:string, createdByUserId?:number, createdDateTime?:Date, lastModifiedByUserId?:number, lastModifiedDatetime?:Date, deletedByUserId?:number, deletedDatetime?:Date, isDeleted?:boolean) {
|
public constructor() {
|
||||||
if (typeof(id) == "number" && typeof(partyRef) == "string" && typeof(name) == "string" && typeof(createdByUserId) == "number" && createdDateTime instanceof Date && typeof(lastModifiedByUserId) == "number" && lastModifiedDatetime instanceof Date && typeof(deletedByUserId) == "number" && deletedDatetime instanceof Date && typeof(isDeleted) == "boolean") {
|
|
||||||
this.Id = id;
|
|
||||||
this.PartyRef = partyRef;
|
|
||||||
this.Name = name;
|
|
||||||
this.CreatedByUserId = createdByUserId;
|
|
||||||
this.CreatedDatetime = createdDateTime;
|
|
||||||
this.LastModifiedByUserId = lastModifiedByUserId;
|
|
||||||
this.LastModifiedDatetime = lastModifiedDatetime;
|
|
||||||
this.DeletedByUserId = deletedByUserId;
|
|
||||||
this.DeletedDatetime = deletedDatetime;
|
|
||||||
this.IsDeleted = isDeleted;
|
|
||||||
} else {
|
|
||||||
this.Id = Number.MIN_VALUE;
|
this.Id = Number.MIN_VALUE;
|
||||||
this.PartyRef = "";
|
this.PartyRef = "";
|
||||||
this.Name = "";
|
this.Name = "";
|
||||||
|
@ -31,4 +19,3 @@ export default class Party {
|
||||||
this.IsDeleted = false;
|
this.IsDeleted = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
|
@ -11,6 +11,7 @@ export default class User {
|
||||||
public PasswordHash:string;
|
public PasswordHash:string;
|
||||||
public APIKey:string;
|
public APIKey:string;
|
||||||
public HasUsedClient:boolean;
|
public HasUsedClient:boolean;
|
||||||
|
public DameCount:number;
|
||||||
public CreatedByUserId:number;
|
public CreatedByUserId:number;
|
||||||
public CreatedDatetime:Date;
|
public CreatedDatetime:Date;
|
||||||
public LastModifiedByUserId?:number;
|
public LastModifiedByUserId?:number;
|
||||||
|
@ -27,6 +28,7 @@ export default class User {
|
||||||
this.PasswordSalt = "";
|
this.PasswordSalt = "";
|
||||||
this.APIKey = "";
|
this.APIKey = "";
|
||||||
this.HasUsedClient = false;
|
this.HasUsedClient = false;
|
||||||
|
this.DameCount = 0;
|
||||||
this.CreatedByUserId = Number.MIN_VALUE;
|
this.CreatedByUserId = Number.MIN_VALUE;
|
||||||
this.CreatedDatetime = new Date(0);
|
this.CreatedDatetime = new Date(0);
|
||||||
this.IsDeleted = false;
|
this.IsDeleted = false;
|
||||||
|
|
|
@ -11,20 +11,7 @@ export default class UserParty {
|
||||||
public DeletedDatetime?:Date;
|
public DeletedDatetime?:Date;
|
||||||
public IsDeleted:boolean;
|
public IsDeleted:boolean;
|
||||||
|
|
||||||
public constructor(id?:number, userId?:number, partyId?:number, isActive?:boolean, createdByUserId?:number, createdDateTime?:Date, lastModifiedByUserId?:number, lastModifiedDatetime?:Date, deletedByUserId?:number, deletedDatetime?:Date, isDeleted?:boolean) {
|
public constructor() {
|
||||||
if (typeof(id) == "number" && typeof(userId) == "number" && typeof(partyId) == "number" && typeof(isActive) === "boolean" && typeof(createdByUserId) == "number" && createdDateTime instanceof Date && typeof(lastModifiedByUserId) == "number" && lastModifiedDatetime instanceof Date && typeof(deletedByUserId) == "number" && deletedDatetime instanceof Date && typeof(isDeleted) == "boolean") {
|
|
||||||
this.Id = id;
|
|
||||||
this.UserId = userId;
|
|
||||||
this.PartyId = partyId;
|
|
||||||
this.IsActive = isActive;
|
|
||||||
this.CreatedByUserId = createdByUserId;
|
|
||||||
this.CreatedDatetime = createdDateTime;
|
|
||||||
this.LastModifiedByUserId = lastModifiedByUserId;
|
|
||||||
this.LastModifiedDatetime = lastModifiedDatetime;
|
|
||||||
this.DeletedByUserId = deletedByUserId;
|
|
||||||
this.DeletedDatetime = deletedDatetime;
|
|
||||||
this.IsDeleted = isDeleted;
|
|
||||||
} else {
|
|
||||||
this.Id = Number.MIN_VALUE;
|
this.Id = Number.MIN_VALUE;
|
||||||
this.UserId = Number.MIN_VALUE;
|
this.UserId = Number.MIN_VALUE;
|
||||||
this.PartyId = Number.MIN_VALUE;
|
this.PartyId = Number.MIN_VALUE;
|
||||||
|
@ -34,4 +21,3 @@ export default class UserParty {
|
||||||
this.IsDeleted = false;
|
this.IsDeleted = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
13
server/entities/list/UserBadgeListItem.ts
Normal file
13
server/entities/list/UserBadgeListItem.ts
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
export default class UserBadgeListItem {
|
||||||
|
public Id: number;
|
||||||
|
public BadgeId: number;
|
||||||
|
public Username: string;
|
||||||
|
public UnlockDatetime: Date;
|
||||||
|
|
||||||
|
public constructor() {
|
||||||
|
this.Id = Number.MIN_VALUE;
|
||||||
|
this.BadgeId = Number.MIN_VALUE;
|
||||||
|
this.Username = "";
|
||||||
|
this.UnlockDatetime = new Date(0);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
export default interface AdminIndexViewModel {
|
export default interface AdminIndexViewModel {
|
||||||
userCount: number,
|
userCount: number,
|
||||||
partyCount: number,
|
partyCount: number,
|
||||||
badgeCount: number
|
badgeCount: number,
|
||||||
|
userBadgeCount: number
|
||||||
}
|
}
|
3
server/models/admin/AdminRemoveUserBadgeModel.ts
Normal file
3
server/models/admin/AdminRemoveUserBadgeModel.ts
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
export default interface AdminRemoveUserBadgeModel {
|
||||||
|
id?: string
|
||||||
|
}
|
11
server/models/admin/AdminUserBadgesViewModel.ts
Normal file
11
server/models/admin/AdminUserBadgesViewModel.ts
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
import Badge from "../../entities/Badge";
|
||||||
|
import FunkyArray from "funky-array";
|
||||||
|
import UserBadgeListItem from "../../entities/list/UserBadgeListItem";
|
||||||
|
|
||||||
|
export default interface AdminUserBadgesViewModel {
|
||||||
|
badgeq?: string,
|
||||||
|
userq?: string,
|
||||||
|
|
||||||
|
badges: Array<Badge>,
|
||||||
|
unlockByBadgeId: FunkyArray<number, Array<UserBadgeListItem>>
|
||||||
|
}
|
|
@ -4,10 +4,10 @@ import RepoBase from "./RepoBase";
|
||||||
|
|
||||||
export default abstract class BadgeRepo {
|
export default abstract class BadgeRepo {
|
||||||
public static async selectAll() {
|
public static async selectAll() {
|
||||||
const dbUser = await Database.Instance.query("SELECT * FROM Badge WHERE IsDeleted = 0");
|
const dbBadge = await Database.Instance.query("SELECT * FROM Badge WHERE IsDeleted = 0", []);
|
||||||
const badges = new Array<Badge>();
|
const badges = new Array<Badge>();
|
||||||
|
|
||||||
for (const row of dbUser) {
|
for (const row of dbBadge) {
|
||||||
const badge = new Badge();
|
const badge = new Badge();
|
||||||
populateBadgeFromDB(badge, row);
|
populateBadgeFromDB(badge, row);
|
||||||
badges.push(badge);
|
badges.push(badge);
|
||||||
|
@ -28,9 +28,22 @@ export default abstract class BadgeRepo {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async selectBadgeCount() {
|
public static async selectBadgeCount() {
|
||||||
const countResult = await Database.Instance.query("SELECT COUNT(Id) FROM `Badge` WHERE IsDeleted = 0 LIMIT 1");
|
const countResult = await Database.Instance.query("SELECT COUNT(Id) FROM `Badge` WHERE IsDeleted = 0 LIMIT 1", []);
|
||||||
|
|
||||||
return countResult[0]["COUNT(Id)"];
|
return countResult[0]["COUNT(Id)"] as number;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async selectList(query?: string) {
|
||||||
|
const dbBadge = await Database.Instance.query("SELECT * FROM Badge WHERE IsDeleted = 0 AND Name LIKE ?", [ query ? `%${query}%` : "%%" ]);
|
||||||
|
const badges = new Array<Badge>();
|
||||||
|
|
||||||
|
for (const row of dbBadge) {
|
||||||
|
const badge = new Badge();
|
||||||
|
populateBadgeFromDB(badge, row);
|
||||||
|
badges.push(badge);
|
||||||
|
}
|
||||||
|
|
||||||
|
return badges;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async insertUpdate(badge:Badge) {
|
public static async insertUpdate(badge:Badge) {
|
||||||
|
|
|
@ -58,7 +58,7 @@ export default class PartyRepo {
|
||||||
public static async selectPartyCount() {
|
public static async selectPartyCount() {
|
||||||
const countResult = await Database.Instance.query("SELECT COUNT(Id) FROM `Party` WHERE IsDeleted = 0 LIMIT 1");
|
const countResult = await Database.Instance.query("SELECT COUNT(Id) FROM `Party` WHERE IsDeleted = 0 LIMIT 1");
|
||||||
|
|
||||||
return countResult[0]["COUNT(Id)"];
|
return countResult[0]["COUNT(Id)"] as number;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async insertUpdate(party:Party) {
|
public static async insertUpdate(party:Party) {
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import UserBadgeListItem from "../entities/list/UserBadgeListItem";
|
||||||
import UserBadge from "../entities/UserBadge";
|
import UserBadge from "../entities/UserBadge";
|
||||||
import Database from "../objects/Database";
|
import Database from "../objects/Database";
|
||||||
import RepoBase from "./RepoBase";
|
import RepoBase from "./RepoBase";
|
||||||
|
@ -14,6 +15,19 @@ export default abstract class UserBadgeRepo {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static async selectAll() {
|
||||||
|
const dbUserBadge = await Database.Instance.query("SELECT * FROM UserBadge WHERE IsDeleted = 0", []);
|
||||||
|
const userBadges = new Array<UserBadge>();
|
||||||
|
|
||||||
|
for (const row of dbUserBadge) {
|
||||||
|
const userBadge = new UserBadge();
|
||||||
|
populateUserBadgeFromDB(userBadge, row);
|
||||||
|
userBadges.push(userBadge);
|
||||||
|
}
|
||||||
|
|
||||||
|
return userBadges;
|
||||||
|
}
|
||||||
|
|
||||||
public static async selectByUserId(userId:number) {
|
public static async selectByUserId(userId:number) {
|
||||||
const dbUserBadge = await Database.Instance.query("SELECT * FROM UserBadge WHERE UserId = ? AND IsDeleted = 0", [ userId ]);
|
const dbUserBadge = await Database.Instance.query("SELECT * FROM UserBadge WHERE UserId = ? AND IsDeleted = 0", [ userId ]);
|
||||||
const userBadges = new Array<UserBadge>();
|
const userBadges = new Array<UserBadge>();
|
||||||
|
@ -38,6 +52,25 @@ export default abstract class UserBadgeRepo {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static async selectUnlockedBadgeCount() {
|
||||||
|
const countResult = await Database.Instance.query("SELECT COUNT(Id) FROM `UserBadge` WHERE IsDeleted = 0 LIMIT 1", []);
|
||||||
|
|
||||||
|
return countResult[0]["COUNT(Id)"] as number;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async selectUnlockList(badgeQuery?: string, userQuery?: string) {
|
||||||
|
const dbUserBadgeListItems = await Database.Instance.query('SELECT UserBadge.Id AS "Id", Badge.Id AS "BadgeId", `User`.`Username`, UserBadge.CreatedDatetime AS "UnlockDatetime" FROM UserBadge JOIN Badge ON Badge.Id = UserBadge.BadgeId AND Badge.IsDeleted = 0 JOIN `User` ON `User`.Id = UserBadge.UserId AND `User`.IsDeleted = 0 WHERE UserBadge.IsDeleted = 0 AND `User`.`Username` LIKE ? AND Badge.Name LIKE ?;', [ userQuery ? `%${userQuery}%` : "%%", badgeQuery ? `%${badgeQuery}%` : "%%" ]);
|
||||||
|
const userBadgeListItems = new Array<UserBadgeListItem>();
|
||||||
|
|
||||||
|
for (const row of dbUserBadgeListItems) {
|
||||||
|
const userBadgeListItem = new UserBadgeListItem();
|
||||||
|
populateUserBadgeListItemFromDB(userBadgeListItem, row);
|
||||||
|
userBadgeListItems.push(userBadgeListItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
return userBadgeListItems;
|
||||||
|
}
|
||||||
|
|
||||||
public static async insertUpdate(userBadge:UserBadge) {
|
public static async insertUpdate(userBadge:UserBadge) {
|
||||||
if (userBadge.Id === Number.MIN_VALUE) {
|
if (userBadge.Id === Number.MIN_VALUE) {
|
||||||
userBadge.Id = (await Database.Instance.query("INSERT UserBadge (UserId, BadgeId, CreatedByUserId, CreatedDatetime, LastModifiedByUserId, LastModifiedDatetime, DeletedByUserId, DeletedDatetime, IsDeleted) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING Id;", [
|
userBadge.Id = (await Database.Instance.query("INSERT UserBadge (UserId, BadgeId, CreatedByUserId, CreatedDatetime, LastModifiedByUserId, LastModifiedDatetime, DeletedByUserId, DeletedDatetime, IsDeleted) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING Id;", [
|
||||||
|
@ -53,15 +86,22 @@ export default abstract class UserBadgeRepo {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function populateUserBadgeFromDB(userBadge:UserBadge, dbUser:any) {
|
function populateUserBadgeFromDB(userBadge:UserBadge, dbBadge:any) {
|
||||||
userBadge.Id = dbUser.Id;
|
userBadge.Id = dbBadge.Id;
|
||||||
userBadge.UserId = dbUser.UserId;
|
userBadge.UserId = dbBadge.UserId;
|
||||||
userBadge.BadgeId = dbUser.BadgeId;
|
userBadge.BadgeId = dbBadge.BadgeId;
|
||||||
userBadge.CreatedByUserId = dbUser.CreatedByUserId;
|
userBadge.CreatedByUserId = dbBadge.CreatedByUserId;
|
||||||
userBadge.CreatedDatetime = new Date(dbUser.CreatedDatetime);
|
userBadge.CreatedDatetime = new Date(dbBadge.CreatedDatetime);
|
||||||
userBadge.LastModifiedByUserId = dbUser.LastModifiedByUserId;
|
userBadge.LastModifiedByUserId = dbBadge.LastModifiedByUserId;
|
||||||
userBadge.LastModifiedDatetime = RepoBase.convertNullableDatetimeIntToDate(dbUser.LastModifiedDatetime);
|
userBadge.LastModifiedDatetime = RepoBase.convertNullableDatetimeIntToDate(dbBadge.LastModifiedDatetime);
|
||||||
userBadge.DeletedByUserId = dbUser.DeletedByUserId;
|
userBadge.DeletedByUserId = dbBadge.DeletedByUserId;
|
||||||
userBadge.DeletedDatetime = RepoBase.convertNullableDatetimeIntToDate(dbUser.DeletedDatetime);
|
userBadge.DeletedDatetime = RepoBase.convertNullableDatetimeIntToDate(dbBadge.DeletedDatetime);
|
||||||
userBadge.IsDeleted = dbUser.IsDeleted[0] === 1;
|
userBadge.IsDeleted = dbBadge.IsDeleted[0] === 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
function populateUserBadgeListItemFromDB(userBadgeListItem:UserBadgeListItem, dbBadgeListItem:any) {
|
||||||
|
userBadgeListItem.Id = dbBadgeListItem.Id;
|
||||||
|
userBadgeListItem.BadgeId = dbBadgeListItem.BadgeId;
|
||||||
|
userBadgeListItem.Username = dbBadgeListItem.Username;
|
||||||
|
userBadgeListItem.UnlockDatetime = new Date(dbBadgeListItem.UnlockDatetime);
|
||||||
}
|
}
|
|
@ -52,7 +52,7 @@ export default class UserRepo {
|
||||||
public static async selectUserCount() {
|
public static async selectUserCount() {
|
||||||
const countResult = await Database.Instance.query("SELECT COUNT(Id) FROM `User` WHERE IsDeleted = 0 LIMIT 1");
|
const countResult = await Database.Instance.query("SELECT COUNT(Id) FROM `User` WHERE IsDeleted = 0 LIMIT 1");
|
||||||
|
|
||||||
return countResult[0]["COUNT(Id)"];
|
return countResult[0]["COUNT(Id)"] as number;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async insertUpdate(user:User) {
|
public static async insertUpdate(user:User) {
|
||||||
|
@ -61,8 +61,8 @@ export default class UserRepo {
|
||||||
user.UserLevel, user.Username, user.PasswordHash, user.PasswordSalt, user.CreatedByUserId, user.CreatedDatetime.getTime(), user.LastModifiedByUserId ?? null, user.LastModifiedDatetime?.getTime() ?? null, user.DeletedByUserId ?? null, user.DeletedDatetime?.getTime() ?? null, Number(user.IsDeleted)
|
user.UserLevel, user.Username, user.PasswordHash, user.PasswordSalt, user.CreatedByUserId, user.CreatedDatetime.getTime(), user.LastModifiedByUserId ?? null, user.LastModifiedDatetime?.getTime() ?? null, user.DeletedByUserId ?? null, user.DeletedDatetime?.getTime() ?? null, Number(user.IsDeleted)
|
||||||
]))[0]["Id"];
|
]))[0]["Id"];
|
||||||
} else {
|
} else {
|
||||||
await Database.Instance.query(`UPDATE User SET UserLevelId = ?, Username = ?, PasswordHash = ?, PasswordSalt = ?, APIKey = ?, HasUsedClient = ?, CreatedByUserId = ?, CreatedDatetime = ?, LastModifiedByUserId = ?, LastModifiedDatetime = ?, DeletedByUserId = ?, DeletedDatetime = ?, IsDeleted = ? WHERE Id = ?`, [
|
await Database.Instance.query(`UPDATE User SET UserLevelId = ?, Username = ?, PasswordHash = ?, PasswordSalt = ?, APIKey = ?, HasUsedClient = ?, DameCount = ?, CreatedByUserId = ?, CreatedDatetime = ?, LastModifiedByUserId = ?, LastModifiedDatetime = ?, DeletedByUserId = ?, DeletedDatetime = ?, IsDeleted = ? WHERE Id = ?`, [
|
||||||
user.UserLevel, user.Username, user.PasswordHash, user.PasswordSalt, user.APIKey, Number(user.HasUsedClient), user.CreatedByUserId, user.CreatedDatetime.getTime(), user.LastModifiedByUserId ?? null, user.LastModifiedDatetime?.getTime() ?? null, user.DeletedByUserId ?? null, user.DeletedDatetime?.getTime() ?? null, Number(user.IsDeleted), user.Id
|
user.UserLevel, user.Username, user.PasswordHash, user.PasswordSalt, user.APIKey, Number(user.HasUsedClient), user.DameCount, user.CreatedByUserId, user.CreatedDatetime.getTime(), user.LastModifiedByUserId ?? null, user.LastModifiedDatetime?.getTime() ?? null, user.DeletedByUserId ?? null, user.DeletedDatetime?.getTime() ?? null, Number(user.IsDeleted), user.Id
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -78,6 +78,7 @@ function populateUserFromDB(user:User, dbUser:any) {
|
||||||
user.PasswordSalt = dbUser.PasswordSalt;
|
user.PasswordSalt = dbUser.PasswordSalt;
|
||||||
user.APIKey = dbUser.APIKey;
|
user.APIKey = dbUser.APIKey;
|
||||||
user.HasUsedClient = dbUser.HasUsedClient[0] === 1;
|
user.HasUsedClient = dbUser.HasUsedClient[0] === 1;
|
||||||
|
user.DameCount = dbUser.DameCount;
|
||||||
user.CreatedByUserId = dbUser.CreatedByUserId;
|
user.CreatedByUserId = dbUser.CreatedByUserId;
|
||||||
user.CreatedDatetime = new Date(dbUser.CreatedDatetime);
|
user.CreatedDatetime = new Date(dbUser.CreatedDatetime);
|
||||||
user.LastModifiedByUserId = dbUser.LastModifiedByUserId;
|
user.LastModifiedByUserId = dbUser.LastModifiedByUserId;
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit 69fb94332deb5dcc1f485d445c042a3f4a7030b0
|
Subproject commit 7accf5b1c9529490c5713f8dbb9a067cfb2c3d49
|
|
@ -2,6 +2,7 @@ import { Console } from "hsconsole";
|
||||||
import Badge from "../entities/Badge";
|
import Badge from "../entities/Badge";
|
||||||
import BadgeRepo from "../repos/BadgeRepo";
|
import BadgeRepo from "../repos/BadgeRepo";
|
||||||
import BadgeCache from "../objects/BadgeCache";
|
import BadgeCache from "../objects/BadgeCache";
|
||||||
|
import UserBadgeRepo from "../repos/UserBadgeRepo";
|
||||||
|
|
||||||
export default abstract class BadgeService {
|
export default abstract class BadgeService {
|
||||||
public static async SaveBadge(currentUserId:number, id:number | undefined, name:string, description:string, imageUrl: string, forUrl:string, isSecret: boolean) {
|
public static async SaveBadge(currentUserId:number, id:number | undefined, name:string, description:string, imageUrl: string, forUrl:string, isSecret: boolean) {
|
||||||
|
@ -51,6 +52,15 @@ export default abstract class BadgeService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static async LoadList(query?: string) {
|
||||||
|
try {
|
||||||
|
return await BadgeRepo.selectList(query);
|
||||||
|
} catch (e) {
|
||||||
|
Console.printError(`MultiProbe server service error:\n${e}`);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static async DeleteBadge(currentUserId:number, id: number) {
|
public static async DeleteBadge(currentUserId:number, id: number) {
|
||||||
const badge = await BadgeRepo.selectById(id);
|
const badge = await BadgeRepo.selectById(id);
|
||||||
if (badge == null) {
|
if (badge == null) {
|
||||||
|
@ -72,4 +82,31 @@ export default abstract class BadgeService {
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static async GetUnlockedBadgeCount() {
|
||||||
|
try {
|
||||||
|
return await UserBadgeRepo.selectUnlockedBadgeCount();
|
||||||
|
} catch (e) {
|
||||||
|
Console.printError(`MultiProbe server service error:\n${e}`);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async GetAllUnlockedBadges() {
|
||||||
|
try {
|
||||||
|
return await UserBadgeRepo.selectAll();
|
||||||
|
} catch (e) {
|
||||||
|
Console.printError(`MultiProbe server service error:\n${e}`);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async GetUnlockedBadgeList(badgeQuery?: string, userQuery?: string) {
|
||||||
|
try {
|
||||||
|
return await UserBadgeRepo.selectUnlockList(badgeQuery, userQuery);
|
||||||
|
} catch (e) {
|
||||||
|
Console.printError(`MultiProbe server service error:\n${e}`);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -253,6 +253,44 @@ export default class UserService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static async DeleteUnlockedBadge(currentUserId:number, userBadgeId:number) {
|
||||||
|
try {
|
||||||
|
let userBadge = await UserBadgeRepo.selectById(userBadgeId);
|
||||||
|
if (!userBadge) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
userBadge.DeletedByUserId = currentUserId;
|
||||||
|
userBadge.DeletedDatetime = new Date();
|
||||||
|
userBadge.IsDeleted = true;
|
||||||
|
|
||||||
|
await UserBadgeRepo.insertUpdate(userBadge)
|
||||||
|
|
||||||
|
return userBadge;
|
||||||
|
} catch (e) {
|
||||||
|
Console.printError(`MultiProbe server service error:\n${e}`);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async IncrementDameCount(currentUserId:number) {
|
||||||
|
try {
|
||||||
|
const user = await UserRepo.selectById(currentUserId);
|
||||||
|
if (!user) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
user.DameCount++;
|
||||||
|
user.LastModifiedByUserId = currentUserId;
|
||||||
|
user.LastModifiedDatetime = new Date();
|
||||||
|
|
||||||
|
return await UserRepo.insertUpdate(user);
|
||||||
|
} catch (e) {
|
||||||
|
Console.printError(`MultiProbe server service error:\n${e}`);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static async SaveUserClientLoginFlag(currentUserId:number) {
|
public static async SaveUserClientLoginFlag(currentUserId:number) {
|
||||||
try {
|
try {
|
||||||
const user = await UserRepo.selectById(currentUserId);
|
const user = await UserRepo.selectById(currentUserId);
|
||||||
|
|
|
@ -26,7 +26,6 @@
|
||||||
<thead>
|
<thead>
|
||||||
<th> </th>
|
<th> </th>
|
||||||
<th>Name</th>
|
<th>Name</th>
|
||||||
<th>For URL</th>
|
|
||||||
<th style="white-space:nowrap">Secret</th>
|
<th style="white-space:nowrap">Secret</th>
|
||||||
<th> </th>
|
<th> </th>
|
||||||
</thead>
|
</thead>
|
||||||
|
@ -35,7 +34,6 @@
|
||||||
<tr>
|
<tr>
|
||||||
<td class="align-middle"><img style="image-rendering:pixelated" src="<%= badge.ImageUrl.trim().length === 0 ? "/img/missing.png" : badge.ImageUrl %>" width="32" height="32" /></td>
|
<td class="align-middle"><img style="image-rendering:pixelated" src="<%= badge.ImageUrl.trim().length === 0 ? "/img/missing.png" : badge.ImageUrl %>" width="32" height="32" /></td>
|
||||||
<td class="align-middle"><%= badge.Name %></td>
|
<td class="align-middle"><%= badge.Name %></td>
|
||||||
<td class="align-middle"><a href="<%= badge.ForUrl %>"><%= badge.ForUrl %></a></td>
|
|
||||||
<td class="align-middle text-center"><%= badge.IsSecret ? "Yes" : "No" %></td>
|
<td class="align-middle text-center"><%= badge.IsSecret ? "Yes" : "No" %></td>
|
||||||
<td class="text-end text-nowrap align-middle">
|
<td class="text-end text-nowrap align-middle">
|
||||||
<a class="btn btn-sm btn-primary" href="/admin/badge?id=<%= badge.Id %>">Edit</a>
|
<a class="btn btn-sm btn-primary" href="/admin/badge?id=<%= badge.Id %>">Edit</a>
|
||||||
|
@ -47,4 +45,5 @@
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<%- include("../base/footer") %>
|
<%- include("../base/footer") %>
|
|
@ -37,7 +37,7 @@
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h4 class="card-title">Badges</h5>
|
<h4 class="card-title">Badges</h5>
|
||||||
<h3 class="card-text mb-0"><%= badgeCount %></h3>
|
<h3 class="card-text mb-0"><%= badgeCount %><small style="font-size:.75rem" class="ps-2"><%= userBadgeCount %> unlocked</small></h3>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -48,6 +48,7 @@
|
||||||
<a class="btn btn-primary btn-lg me-2 mb-3" href="/admin/users">Manage Users</a>
|
<a class="btn btn-primary btn-lg me-2 mb-3" href="/admin/users">Manage Users</a>
|
||||||
<a class="btn btn-primary btn-lg me-2 mb-3" href="/admin/parties">Manage Parties</a>
|
<a class="btn btn-primary btn-lg me-2 mb-3" href="/admin/parties">Manage Parties</a>
|
||||||
<a class="btn btn-primary btn-lg me-2 mb-3" href="/admin/badges">Manage Badges</a>
|
<a class="btn btn-primary btn-lg me-2 mb-3" href="/admin/badges">Manage Badges</a>
|
||||||
|
<a class="btn btn-primary btn-lg me-2 mb-3" href="/admin/userbadges">Manage Unlocked Badges</a>
|
||||||
<a class="btn btn-primary btn-lg me-2 mb-3" href="/admin/websessions">Web Sessions</a>
|
<a class="btn btn-primary btn-lg me-2 mb-3" href="/admin/websessions">Web Sessions</a>
|
||||||
<a class="btn btn-primary btn-lg me-2 mb-3" href="/admin/wssessions">Websocket Sessions</a>
|
<a class="btn btn-primary btn-lg me-2 mb-3" href="/admin/wssessions">Websocket Sessions</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
61
server/views/admin/userbadges.ejs
Normal file
61
server/views/admin/userbadges.ejs
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
<%- include("../base/header", { title: "Unlocked Badge Management", userId: session.userId, isAdmin: true }) %>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col">
|
||||||
|
<nav aria-label="breadcrumb">
|
||||||
|
<ol class="breadcrumb">
|
||||||
|
<li class="breadcrumb-item"><a href="/admin">Admin</a></li>
|
||||||
|
<li class="breadcrumb-item active"><a>Unlocked Badge Management</a></li>
|
||||||
|
</ol>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row mb-3">
|
||||||
|
<div class="col">
|
||||||
|
<h1>Unlocked Badge Management</h1>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col">
|
||||||
|
<form>
|
||||||
|
<label class="form-label" for="badgeq">Badge Name (fuzzy)</label>
|
||||||
|
<input class="form-control mb-3" name="badgeq" id="badgeq" value="<%= typeof(badgeq) === "undefined" ? "" : badgeq %>">
|
||||||
|
<label class="form-label">Username (fuzzy)</label>
|
||||||
|
<input class="form-control" name="userq" id="userq" value="<%= typeof(userq) === "undefined" ? "" : userq %>">
|
||||||
|
<input type="submit" hidden />
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row my-5">
|
||||||
|
<div class="col">
|
||||||
|
<% for (const badge of badges) { %>
|
||||||
|
<% const unlockArray = unlockByBadgeId.get(badge.Id) ?? []; %>
|
||||||
|
<div class="row row-cols-1 mb-3">
|
||||||
|
<div class="col bg-light-subtle p-3">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-auto align-middle"><img style="image-rendering:pixelated" src="<%= badge.ImageUrl.trim().length === 0 ? "/img/missing.png" : badge.ImageUrl %>" width="32" height="32" /></div>
|
||||||
|
<div class="col d-flex align-items-center ps-0"><%= badge.Name %></div>
|
||||||
|
<div class="col-auto d-flex align-items-center pe-4"><%= unlockArray.length %></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col bg-dark-subtle">
|
||||||
|
<% for (const unlock of unlockArray) { %>
|
||||||
|
<div class="row border-bottom">
|
||||||
|
<div class="col p-3 d-flex align-items-center">
|
||||||
|
<%= unlock.Username %>
|
||||||
|
</div>
|
||||||
|
<div class="col-auto p-3">
|
||||||
|
<a class="btn btn-danger" href="/admin/removeuserbadge?id=<%= unlock.Id %>">Remove</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<% } %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<% } %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<%- include("../base/footer") %>
|
|
@ -69,7 +69,7 @@
|
||||||
<% for (const badgeKey of badgeById.keys) { %>
|
<% for (const badgeKey of badgeById.keys) { %>
|
||||||
<% const badge = badgeById.get(badgeKey); const unlockedBadge = unlockedBadgesById.get(badgeKey); %>
|
<% const badge = badgeById.get(badgeKey); const unlockedBadge = unlockedBadgesById.get(badgeKey); %>
|
||||||
<% if (unlockedBadge) { %>
|
<% if (unlockedBadge) { %>
|
||||||
<span class="d-inline-block mb-3" tabindex="0" data-bs-toggle="popover" data-bs-trigger="hover focus" data-bs-placement="top" data-bs-title="<%= badge.Name %>" data-bs-content="<%= badge.Description %><br><small>Unlocked on <%= unlockedBadge.CreatedDatetime.toString().split(" ").slice(1, 4).join(" ") %></small>">
|
<span class="d-inline-block mb-3" tabindex="0" data-bs-toggle="popover" data-bs-trigger="hover focus" data-bs-placement="top" data-bs-title="<%= badge.Name %>" data-bs-content="<%= badge.Description.replaceAll("\r", "").replaceAll("\n", "<br>") %><br><%= badge.Id === 13 ? `<small>You've been Dame'd ${user.DameCount} times</small><br>` : "" %><small>Unlocked on <%= unlockedBadge.CreatedDatetime.toString().split(" ").slice(1, 4).join(" ") %></small>">
|
||||||
<img width="32" height="32" src="<%= badge.ImageUrl %>">
|
<img width="32" height="32" src="<%= badge.ImageUrl %>">
|
||||||
</span>
|
</span>
|
||||||
<% } else if (!badge.IsSecret) { %>
|
<% } else if (!badge.IsSecret) { %>
|
||||||
|
|
Loading…
Reference in a new issue