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==
|
||||
// @name MultiProbe
|
||||
// @namespace https://*.angusnicneven.com/*
|
||||
// @version 20241009.0
|
||||
// @version 20241012.0
|
||||
// @description Probe with friends!
|
||||
// @author tgpholly
|
||||
// @match https://*.angusnicneven.com/*
|
||||
|
@ -55,7 +55,7 @@ console.log("[MP] MultiProbe init");
|
|||
'use strict';
|
||||
|
||||
// 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(".", ""));
|
||||
|
||||
if (!continueRunningScript) {
|
||||
|
@ -799,6 +799,7 @@ mp_button {
|
|||
.writeShortString(apiKey)
|
||||
.writeString(currentPage)
|
||||
.toBuffer());
|
||||
pingStartTime = performance.now();
|
||||
keepAliveInterval = setInterval(() => {
|
||||
pingStartTime = performance.now();
|
||||
ws.send(keepAlivePacket);
|
||||
|
|
|
@ -6,23 +6,28 @@ import AdminExpireSessionModel from "../models/admin/AdminExpireSessionModel";
|
|||
import AdminIndexViewModel from "../models/admin/AdminIndexViewModel";
|
||||
import AdminPartiesViewModel from "../models/admin/AdminPartiesViewModel";
|
||||
import AdminPartyViewModel from "../models/admin/AdminPartyViewModel";
|
||||
import AdminUserBadgesViewModel from "../models/admin/AdminUserBadgesViewModel";
|
||||
import AdminUsersViewModel from "../models/admin/AdminUsersViewModel";
|
||||
import AdminUserViewModel from "../models/admin/AdminUserViewModel";
|
||||
import AdminWebSessionsViewModel from "../models/admin/AdminWebSessionsViewModel";
|
||||
import AdminWsSessionsViewModel from "../models/admin/AdminWsSessionsViewModel";
|
||||
import FunkyArray from "funky-array";
|
||||
import Session from "../objects/Session";
|
||||
import WsData from "../objects/WsData";
|
||||
import BadgeService from "../services/BadgeService";
|
||||
import PartyService from "../services/PartyService";
|
||||
import UserService from "../services/UserService";
|
||||
import Controller from "./Controller";
|
||||
import UserBadgeListItem from "../entities/list/UserBadgeListItem";
|
||||
import AdminRemoveUserBadgeModel from "../models/admin/AdminRemoveUserBadgeModel";
|
||||
|
||||
export default class AdminController_Auth$Admin extends Controller {
|
||||
public async Index_Get() {
|
||||
const adminIndexViewModel: AdminIndexViewModel = {
|
||||
userCount: await UserService.GetUserCount(),
|
||||
partyCount: await PartyService.GetPartyCount(),
|
||||
badgeCount: await BadgeService.GetBadgeCount()
|
||||
badgeCount: await BadgeService.GetBadgeCount(),
|
||||
userBadgeCount: await BadgeService.GetUnlockedBadgeCount()
|
||||
};
|
||||
|
||||
return this.view(adminIndexViewModel);
|
||||
|
@ -178,4 +183,34 @@ export default class AdminController_Auth$Admin extends Controller {
|
|||
|
||||
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,25 +10,12 @@ export default class Party {
|
|||
public DeletedDatetime?:Date;
|
||||
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) {
|
||||
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.PartyRef = "";
|
||||
this.Name = "";
|
||||
this.CreatedByUserId = Number.MIN_VALUE;
|
||||
this.CreatedDatetime = new Date(0);
|
||||
this.IsDeleted = false;
|
||||
}
|
||||
public constructor() {
|
||||
this.Id = Number.MIN_VALUE;
|
||||
this.PartyRef = "";
|
||||
this.Name = "";
|
||||
this.CreatedByUserId = Number.MIN_VALUE;
|
||||
this.CreatedDatetime = new Date(0);
|
||||
this.IsDeleted = false;
|
||||
}
|
||||
}
|
|
@ -11,6 +11,7 @@ export default class User {
|
|||
public PasswordHash:string;
|
||||
public APIKey:string;
|
||||
public HasUsedClient:boolean;
|
||||
public DameCount:number;
|
||||
public CreatedByUserId:number;
|
||||
public CreatedDatetime:Date;
|
||||
public LastModifiedByUserId?:number;
|
||||
|
@ -27,6 +28,7 @@ export default class User {
|
|||
this.PasswordSalt = "";
|
||||
this.APIKey = "";
|
||||
this.HasUsedClient = false;
|
||||
this.DameCount = 0;
|
||||
this.CreatedByUserId = Number.MIN_VALUE;
|
||||
this.CreatedDatetime = new Date(0);
|
||||
this.IsDeleted = false;
|
||||
|
|
|
@ -11,27 +11,13 @@ export default class UserParty {
|
|||
public DeletedDatetime?:Date;
|
||||
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) {
|
||||
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.UserId = Number.MIN_VALUE;
|
||||
this.PartyId = Number.MIN_VALUE;
|
||||
this.IsActive = false;
|
||||
this.CreatedByUserId = Number.MIN_VALUE;
|
||||
this.CreatedDatetime = new Date(0);
|
||||
this.IsDeleted = false;
|
||||
}
|
||||
public constructor() {
|
||||
this.Id = Number.MIN_VALUE;
|
||||
this.UserId = Number.MIN_VALUE;
|
||||
this.PartyId = Number.MIN_VALUE;
|
||||
this.IsActive = false;
|
||||
this.CreatedByUserId = Number.MIN_VALUE;
|
||||
this.CreatedDatetime = new Date(0);
|
||||
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 {
|
||||
userCount: 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 {
|
||||
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>();
|
||||
|
||||
for (const row of dbUser) {
|
||||
for (const row of dbBadge) {
|
||||
const badge = new Badge();
|
||||
populateBadgeFromDB(badge, row);
|
||||
badges.push(badge);
|
||||
|
@ -28,9 +28,22 @@ export default abstract class BadgeRepo {
|
|||
}
|
||||
|
||||
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) {
|
||||
|
|
|
@ -58,7 +58,7 @@ export default class PartyRepo {
|
|||
public static async selectPartyCount() {
|
||||
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) {
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import UserBadgeListItem from "../entities/list/UserBadgeListItem";
|
||||
import UserBadge from "../entities/UserBadge";
|
||||
import Database from "../objects/Database";
|
||||
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) {
|
||||
const dbUserBadge = await Database.Instance.query("SELECT * FROM UserBadge WHERE UserId = ? AND IsDeleted = 0", [ userId ]);
|
||||
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) {
|
||||
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;", [
|
||||
|
@ -53,15 +86,22 @@ export default abstract class UserBadgeRepo {
|
|||
}
|
||||
}
|
||||
|
||||
function populateUserBadgeFromDB(userBadge:UserBadge, dbUser:any) {
|
||||
userBadge.Id = dbUser.Id;
|
||||
userBadge.UserId = dbUser.UserId;
|
||||
userBadge.BadgeId = dbUser.BadgeId;
|
||||
userBadge.CreatedByUserId = dbUser.CreatedByUserId;
|
||||
userBadge.CreatedDatetime = new Date(dbUser.CreatedDatetime);
|
||||
userBadge.LastModifiedByUserId = dbUser.LastModifiedByUserId;
|
||||
userBadge.LastModifiedDatetime = RepoBase.convertNullableDatetimeIntToDate(dbUser.LastModifiedDatetime);
|
||||
userBadge.DeletedByUserId = dbUser.DeletedByUserId;
|
||||
userBadge.DeletedDatetime = RepoBase.convertNullableDatetimeIntToDate(dbUser.DeletedDatetime);
|
||||
userBadge.IsDeleted = dbUser.IsDeleted[0] === 1;
|
||||
function populateUserBadgeFromDB(userBadge:UserBadge, dbBadge:any) {
|
||||
userBadge.Id = dbBadge.Id;
|
||||
userBadge.UserId = dbBadge.UserId;
|
||||
userBadge.BadgeId = dbBadge.BadgeId;
|
||||
userBadge.CreatedByUserId = dbBadge.CreatedByUserId;
|
||||
userBadge.CreatedDatetime = new Date(dbBadge.CreatedDatetime);
|
||||
userBadge.LastModifiedByUserId = dbBadge.LastModifiedByUserId;
|
||||
userBadge.LastModifiedDatetime = RepoBase.convertNullableDatetimeIntToDate(dbBadge.LastModifiedDatetime);
|
||||
userBadge.DeletedByUserId = dbBadge.DeletedByUserId;
|
||||
userBadge.DeletedDatetime = RepoBase.convertNullableDatetimeIntToDate(dbBadge.DeletedDatetime);
|
||||
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() {
|
||||
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) {
|
||||
|
@ -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)
|
||||
]))[0]["Id"];
|
||||
} else {
|
||||
await Database.Instance.query(`UPDATE User SET UserLevelId = ?, Username = ?, PasswordHash = ?, PasswordSalt = ?, APIKey = ?, HasUsedClient = ?, 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
|
||||
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.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.APIKey = dbUser.APIKey;
|
||||
user.HasUsedClient = dbUser.HasUsedClient[0] === 1;
|
||||
user.DameCount = dbUser.DameCount;
|
||||
user.CreatedByUserId = dbUser.CreatedByUserId;
|
||||
user.CreatedDatetime = new Date(dbUser.CreatedDatetime);
|
||||
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 BadgeRepo from "../repos/BadgeRepo";
|
||||
import BadgeCache from "../objects/BadgeCache";
|
||||
import UserBadgeRepo from "../repos/UserBadgeRepo";
|
||||
|
||||
export default abstract class BadgeService {
|
||||
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) {
|
||||
const badge = await BadgeRepo.selectById(id);
|
||||
if (badge == null) {
|
||||
|
@ -72,4 +82,31 @@ export default abstract class BadgeService {
|
|||
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) {
|
||||
try {
|
||||
const user = await UserRepo.selectById(currentUserId);
|
||||
|
|
|
@ -26,7 +26,6 @@
|
|||
<thead>
|
||||
<th> </th>
|
||||
<th>Name</th>
|
||||
<th>For URL</th>
|
||||
<th style="white-space:nowrap">Secret</th>
|
||||
<th> </th>
|
||||
</thead>
|
||||
|
@ -35,7 +34,6 @@
|
|||
<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"><%= 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="text-end text-nowrap align-middle">
|
||||
<a class="btn btn-sm btn-primary" href="/admin/badge?id=<%= badge.Id %>">Edit</a>
|
||||
|
@ -47,4 +45,5 @@
|
|||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<%- include("../base/footer") %>
|
|
@ -37,7 +37,7 @@
|
|||
<div class="card">
|
||||
<div class="card-body">
|
||||
<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>
|
||||
|
@ -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/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/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/wssessions">Websocket Sessions</a>
|
||||
</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) { %>
|
||||
<% const badge = badgeById.get(badgeKey); const unlockedBadge = unlockedBadgesById.get(badgeKey); %>
|
||||
<% 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 %>">
|
||||
</span>
|
||||
<% } else if (!badge.IsSecret) { %>
|
||||
|
|
Loading…
Reference in a new issue