diff --git a/client/Terminal-00-Multiuser.user.js b/client/Terminal-00-Multiuser.user.js index 3e4b81e..b6d3a20 100644 --- a/client/Terminal-00-Multiuser.user.js +++ b/client/Terminal-00-Multiuser.user.js @@ -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); diff --git a/server/controller/AdminController.ts b/server/controller/AdminController.ts index 9973a3a..15289ad 100644 --- a/server/controller/AdminController.ts +++ b/server/controller/AdminController.ts @@ -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>(); + let unlockedBadgeByBadgeIdArray: Array | undefined; + for (const unlockedBadgeListItem of unlockedBadgeListItems) { + if (!(unlockedBadgeByBadgeIdArray = unlockedBadgeByBadgeId.get(unlockedBadgeListItem.BadgeId))) { + unlockedBadgeByBadgeId.set(unlockedBadgeListItem.BadgeId, unlockedBadgeByBadgeIdArray = new Array()); + } + + 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"); + } } \ No newline at end of file diff --git a/server/entities/Party.ts b/server/entities/Party.ts index 6c9fdac..f255e02 100644 --- a/server/entities/Party.ts +++ b/server/entities/Party.ts @@ -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; } } \ No newline at end of file diff --git a/server/entities/User.ts b/server/entities/User.ts index 94001c4..a699195 100644 --- a/server/entities/User.ts +++ b/server/entities/User.ts @@ -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; diff --git a/server/entities/UserParty.ts b/server/entities/UserParty.ts index 085013d..c2ed77a 100644 --- a/server/entities/UserParty.ts +++ b/server/entities/UserParty.ts @@ -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; } } \ No newline at end of file diff --git a/server/entities/list/UserBadgeListItem.ts b/server/entities/list/UserBadgeListItem.ts new file mode 100644 index 0000000..8a8a21f --- /dev/null +++ b/server/entities/list/UserBadgeListItem.ts @@ -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); + } +} \ No newline at end of file diff --git a/server/models/admin/AdminIndexViewModel.ts b/server/models/admin/AdminIndexViewModel.ts index e263537..2ccbee7 100644 --- a/server/models/admin/AdminIndexViewModel.ts +++ b/server/models/admin/AdminIndexViewModel.ts @@ -1,5 +1,6 @@ export default interface AdminIndexViewModel { userCount: number, partyCount: number, - badgeCount: number + badgeCount: number, + userBadgeCount: number } \ No newline at end of file diff --git a/server/models/admin/AdminRemoveUserBadgeModel.ts b/server/models/admin/AdminRemoveUserBadgeModel.ts new file mode 100644 index 0000000..7645694 --- /dev/null +++ b/server/models/admin/AdminRemoveUserBadgeModel.ts @@ -0,0 +1,3 @@ +export default interface AdminRemoveUserBadgeModel { + id?: string +} \ No newline at end of file diff --git a/server/models/admin/AdminUserBadgesViewModel.ts b/server/models/admin/AdminUserBadgesViewModel.ts new file mode 100644 index 0000000..1744360 --- /dev/null +++ b/server/models/admin/AdminUserBadgesViewModel.ts @@ -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, + unlockByBadgeId: FunkyArray> +} \ No newline at end of file diff --git a/server/repos/BadgeRepo.ts b/server/repos/BadgeRepo.ts index 6124dba..95a07da 100644 --- a/server/repos/BadgeRepo.ts +++ b/server/repos/BadgeRepo.ts @@ -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(); - 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(); + + for (const row of dbBadge) { + const badge = new Badge(); + populateBadgeFromDB(badge, row); + badges.push(badge); + } + + return badges; } public static async insertUpdate(badge:Badge) { diff --git a/server/repos/PartyRepo.ts b/server/repos/PartyRepo.ts index 4d1f222..77f03c6 100644 --- a/server/repos/PartyRepo.ts +++ b/server/repos/PartyRepo.ts @@ -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) { diff --git a/server/repos/UserBadgeRepo.ts b/server/repos/UserBadgeRepo.ts index 752adfb..a7db222 100644 --- a/server/repos/UserBadgeRepo.ts +++ b/server/repos/UserBadgeRepo.ts @@ -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(); + + 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(); @@ -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(); + + 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); } \ No newline at end of file diff --git a/server/repos/UserRepo.ts b/server/repos/UserRepo.ts index e3fd807..f516e4a 100644 --- a/server/repos/UserRepo.ts +++ b/server/repos/UserRepo.ts @@ -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; diff --git a/server/scripts b/server/scripts index 69fb943..7accf5b 160000 --- a/server/scripts +++ b/server/scripts @@ -1 +1 @@ -Subproject commit 69fb94332deb5dcc1f485d445c042a3f4a7030b0 +Subproject commit 7accf5b1c9529490c5713f8dbb9a067cfb2c3d49 diff --git a/server/services/BadgeService.ts b/server/services/BadgeService.ts index 26cccc0..985051b 100644 --- a/server/services/BadgeService.ts +++ b/server/services/BadgeService.ts @@ -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; + } + } } \ No newline at end of file diff --git a/server/services/UserService.ts b/server/services/UserService.ts index c0ebb42..209b3b7 100644 --- a/server/services/UserService.ts +++ b/server/services/UserService.ts @@ -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); diff --git a/server/views/admin/badges.ejs b/server/views/admin/badges.ejs index e6e545e..d761cab 100644 --- a/server/views/admin/badges.ejs +++ b/server/views/admin/badges.ejs @@ -26,7 +26,6 @@   Name - For URL Secret   @@ -35,7 +34,6 @@ " width="32" height="32" /> <%= badge.Name %> - <%= badge.ForUrl %> <%= badge.IsSecret ? "Yes" : "No" %> Edit @@ -47,4 +45,5 @@ + <%- include("../base/footer") %> \ No newline at end of file diff --git a/server/views/admin/index.ejs b/server/views/admin/index.ejs index f8a18c7..cae32d2 100644 --- a/server/views/admin/index.ejs +++ b/server/views/admin/index.ejs @@ -37,7 +37,7 @@

Badges

-

<%= badgeCount %>

+

<%= badgeCount %><%= userBadgeCount %> unlocked

@@ -48,6 +48,7 @@ Manage Users Manage Parties Manage Badges + Manage Unlocked Badges Web Sessions Websocket Sessions diff --git a/server/views/admin/userbadges.ejs b/server/views/admin/userbadges.ejs new file mode 100644 index 0000000..c2f105a --- /dev/null +++ b/server/views/admin/userbadges.ejs @@ -0,0 +1,61 @@ +<%- include("../base/header", { title: "Unlocked Badge Management", userId: session.userId, isAdmin: true }) %> + +
+ +
+ +
+
+

Unlocked Badge Management

+
+
+ +
+
+
+ + "> + + "> + +
+
+
+ +
+
+ <% for (const badge of badges) { %> + <% const unlockArray = unlockByBadgeId.get(badge.Id) ?? []; %> +
+
+
+
" width="32" height="32" />
+
<%= badge.Name %>
+
<%= unlockArray.length %>
+
+
+
+ <% for (const unlock of unlockArray) { %> +
+
+ <%= unlock.Username %> +
+
+ Remove +
+
+ <% } %> +
+
+ <% } %> +
+
+ +<%- include("../base/footer") %> \ No newline at end of file diff --git a/server/views/home/home.ejs b/server/views/home/home.ejs index c6aa543..26c272e 100644 --- a/server/views/home/home.ejs +++ b/server/views/home/home.ejs @@ -69,7 +69,7 @@ <% for (const badgeKey of badgeById.keys) { %> <% const badge = badgeById.get(badgeKey); const unlockedBadge = unlockedBadgesById.get(badgeKey); %> <% if (unlockedBadge) { %> - "> + ") %>
<%= badge.Id === 13 ? `You've been Dame'd ${user.DameCount} times
` : "" %>Unlocked on <%= unlockedBadge.CreatedDatetime.toString().split(" ").slice(1, 4).join(" ") %>">
<% } else if (!badge.IsSecret) { %>