allow joining party, creating party, setting and unsetting active party, add Party ui to client
This commit is contained in:
parent
882f16356c
commit
771a5ca97a
9 changed files with 205 additions and 19 deletions
|
@ -17,6 +17,9 @@ import { randomBytes } from "crypto";
|
|||
import SessionUser from "./objects/SessionUser";
|
||||
import PasswordUtility from "./utilities/PasswordUtility";
|
||||
import CreateEditPartyData from "./interfaces/CreateEditPartyData";
|
||||
import JoinPartyData from "./interfaces/JoinPartyData";
|
||||
import UserParty from "./objects/UserParty";
|
||||
import IdData from "./interfaces/IdData";
|
||||
|
||||
Console.customHeader(`MultiProbe server started at ${new Date()}`);
|
||||
|
||||
|
@ -79,8 +82,9 @@ fastify.get("/", async (req, res) => {
|
|||
if (session = validateSession(req.cookies)) {
|
||||
const user = await UserService.GetUser(session.userId);
|
||||
const parties = await UserService.GetUserParties(session.userId);
|
||||
const activeUserParty = await UserService.GetActiveParty(session.userId);
|
||||
if (user) {
|
||||
return res.view("templates/home.ejs", { user, parties });
|
||||
return res.view("templates/home.ejs", { user, parties, activeUserParty });
|
||||
}
|
||||
|
||||
return res.view("templates/index.ejs", { });
|
||||
|
@ -101,13 +105,19 @@ fastify.get("/account/register", async (req, res) => {
|
|||
return res.view("templates/account/register.ejs", { });
|
||||
});
|
||||
|
||||
fastify.get("/account/logout", async (req, res) => {
|
||||
res.clearCookie("MP_SESSION");
|
||||
|
||||
return res.redirect(302, "/");
|
||||
});
|
||||
|
||||
fastify.get("/party/create", async (req, res) => {
|
||||
let session:SessionUser | undefined;
|
||||
if (!(session = validateSession(req.cookies))) {
|
||||
return res.redirect(302, "/");
|
||||
}
|
||||
|
||||
return res.view("templates/party/createedit.ejs", { });
|
||||
return res.view("templates/party/createedit.ejs", { session });
|
||||
});
|
||||
|
||||
fastify.get("/party/join", async (req, res) => {
|
||||
|
@ -116,7 +126,35 @@ fastify.get("/party/join", async (req, res) => {
|
|||
return res.redirect(302, "/");
|
||||
}
|
||||
|
||||
return res.view("templates/party/join.ejs", { });
|
||||
return res.view("templates/party/join.ejs", { session });
|
||||
});
|
||||
|
||||
fastify.get("/party/setactive", async (req, res) => {
|
||||
let session:SessionUser | undefined;
|
||||
if (!(session = validateSession(req.cookies))) {
|
||||
return res.redirect(302, "/");
|
||||
}
|
||||
|
||||
const data = req.query as IdData;
|
||||
const numericId = parseInt(data.id ?? "-1");
|
||||
if (typeof(data.id) !== "string" || isNaN(numericId)) {
|
||||
return res.redirect(302, "/");
|
||||
}
|
||||
|
||||
await UserService.SetActiveParty(session.userId, numericId);
|
||||
|
||||
return res.redirect(302, "/");
|
||||
});
|
||||
|
||||
fastify.get("/party/deactivate", async (req, res) => {
|
||||
let session:SessionUser | undefined;
|
||||
if (!(session = validateSession(req.cookies))) {
|
||||
return res.redirect(302, "/");
|
||||
}
|
||||
|
||||
await UserService.DeactivateCurrentParty(session.userId);
|
||||
|
||||
return res.redirect(302, "/");
|
||||
});
|
||||
|
||||
// Post Methods
|
||||
|
@ -187,12 +225,12 @@ fastify.post("/party/create", async (req, res) => {
|
|||
|
||||
const data = req.body as CreateEditPartyData;
|
||||
if (typeof(data.partyName) !== "string" || typeof(data.partyRef) !== "string" || data.partyName.length === 0 || data.partyRef.length === 0) {
|
||||
return res.view("templates/party/createedit.ejs", { partyName: data.partyName ?? "", partyRef: data.partyRef ?? "" });
|
||||
return res.view("templates/party/createedit.ejs", { session, partyName: data.partyName ?? "", partyRef: data.partyRef ?? "" });
|
||||
}
|
||||
|
||||
const party = await UserService.GetPartyByPartyRef(data.partyRef)
|
||||
if (party != null) {
|
||||
return res.view("templates/party/createedit.ejs", { partyName: data.partyName ?? "", partyRef: data.partyRef ?? "", error: "A group with that Party ID already exists" });
|
||||
return res.view("templates/party/createedit.ejs", { session, partyName: data.partyName ?? "", partyRef: data.partyRef ?? "", error: "A group with that Party ID already exists" });
|
||||
}
|
||||
|
||||
await UserService.CreateParty(session.userId, data.partyName, data.partyRef);
|
||||
|
@ -203,6 +241,36 @@ fastify.post("/party/create", async (req, res) => {
|
|||
}
|
||||
});
|
||||
|
||||
fastify.post("/party/join", async (req, res) => {
|
||||
try {
|
||||
let session:SessionUser | undefined;
|
||||
if (!(session = validateSession(req.cookies))) {
|
||||
return res.redirect(302, "/");
|
||||
}
|
||||
|
||||
const data = req.body as JoinPartyData;
|
||||
if (typeof(data.partyRef) !== "string" || data.partyRef.length === 0) {
|
||||
return res.view("templates/party/join.ejs", { partyRef: data.partyRef ?? "" });
|
||||
}
|
||||
|
||||
const party = await UserService.GetPartyByPartyRef(data.partyRef);
|
||||
if (party == null) {
|
||||
return res.view("templates/party/join.ejs", { session, partyRef: data.partyRef ?? "", error: "That Join Code / Party ID is invalid." });
|
||||
}
|
||||
|
||||
const userPartyExisting = await UserService.GetUserPartyForUser(session.userId, party.Id);
|
||||
if (userPartyExisting != null) {
|
||||
return res.view("templates/party/join.ejs", { session, partyRef: data.partyRef ?? "", error: "You are already in this group." });
|
||||
}
|
||||
|
||||
await UserService.AddUserToParty(session.userId, party.Id);
|
||||
|
||||
return res.redirect(302, "/");
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
});
|
||||
|
||||
// Websocket stuff
|
||||
|
||||
const websocketServer = new WebSocketServer({
|
||||
|
|
3
server/interfaces/IdData.ts
Normal file
3
server/interfaces/IdData.ts
Normal file
|
@ -0,0 +1,3 @@
|
|||
export default interface IdData {
|
||||
id?: string
|
||||
}
|
3
server/interfaces/JoinPartyData.ts
Normal file
3
server/interfaces/JoinPartyData.ts
Normal file
|
@ -0,0 +1,3 @@
|
|||
export default interface JoinPartyData {
|
||||
partyRef: string
|
||||
}
|
|
@ -2,6 +2,7 @@ export default class UserParty {
|
|||
public Id:number;
|
||||
public UserId:number;
|
||||
public PartyId:number;
|
||||
public IsActive:boolean;
|
||||
public CreatedByUserId:number;
|
||||
public CreatedDatetime:Date;
|
||||
public LastModifiedByUserId?:number;
|
||||
|
@ -10,11 +11,12 @@ export default class UserParty {
|
|||
public DeletedDatetime?:Date;
|
||||
public IsDeleted:boolean;
|
||||
|
||||
public constructor(id?:number, userId?:number, partyId?:number, 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(createdByUserId) == "number" && createdDateTime instanceof Date && typeof(lastModifiedByUserId) == "number" && lastModifiedDatetime instanceof Date && typeof(deletedByUserId) == "number" && deletedDatetime instanceof Date && typeof(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;
|
||||
|
@ -26,6 +28,7 @@ export default class UserParty {
|
|||
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;
|
||||
|
|
|
@ -30,14 +30,42 @@ export default class UserPartyRepo {
|
|||
}
|
||||
}
|
||||
|
||||
public static async selectByUserIdPartyId(userId:number, partyId:number) {
|
||||
const dbUserParty = await Database.Instance.query("SELECT * FROM UserParty WHERE UserId = ? AND PartyId = ? AND IsDeleted = 0", [userId, partyId]);
|
||||
if (dbUserParty == null || dbUserParty.length === 0) {
|
||||
return null;
|
||||
} else {
|
||||
const userParty = new UserParty();
|
||||
populateUserPartyFromDB(userParty, dbUserParty[0]);
|
||||
return userParty;
|
||||
}
|
||||
}
|
||||
|
||||
public static async deactivateAll(userId:number) {
|
||||
await Database.Instance.query("UPDATE UserParty SET IsActive = 0, LastModifiedByUserId = ?, LastModifiedDatetime = ? WHERE UserId = ? AND IsActive = 1", [
|
||||
userId, Date.now(), userId
|
||||
]);
|
||||
}
|
||||
|
||||
public static async selectActive(userId:number) {
|
||||
const dbUserParty = await Database.Instance.query("SELECT * FROM UserParty WHERE UserId = ? AND IsActive = 1 AND IsDeleted = 0 LIMIT 1", [userId]);
|
||||
if (dbUserParty == null || dbUserParty.length === 0) {
|
||||
return null;
|
||||
} else {
|
||||
const userParty = new UserParty();
|
||||
populateUserPartyFromDB(userParty, dbUserParty[0]);
|
||||
return userParty;
|
||||
}
|
||||
}
|
||||
|
||||
public static async insertUpdate(userParty:UserParty) {
|
||||
if (userParty.Id === Number.MIN_VALUE) {
|
||||
await Database.Instance.query("INSERT UserParty (UserId, PartyId, CreatedByUserId, CreatedDatetime, LastModifiedByUserId, LastModifiedDatetime, DeletedByUserId, DeletedDatetime, IsDeleted) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)", [
|
||||
userParty.UserId, userParty.PartyId, userParty.CreatedByUserId, userParty.CreatedDatetime.getTime(), userParty.LastModifiedByUserId ?? null, userParty.LastModifiedDatetime?.getTime() ?? null, userParty.DeletedByUserId ?? null, userParty.DeletedDatetime?.getTime() ?? null, Number(userParty.IsDeleted)
|
||||
await Database.Instance.query("INSERT UserParty (UserId, PartyId, IsActive, CreatedByUserId, CreatedDatetime, LastModifiedByUserId, LastModifiedDatetime, DeletedByUserId, DeletedDatetime, IsDeleted) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", [
|
||||
userParty.UserId, userParty.PartyId, Number(userParty.IsActive), userParty.CreatedByUserId, userParty.CreatedDatetime.getTime(), userParty.LastModifiedByUserId ?? null, userParty.LastModifiedDatetime?.getTime() ?? null, userParty.DeletedByUserId ?? null, userParty.DeletedDatetime?.getTime() ?? null, Number(userParty.IsDeleted)
|
||||
]);
|
||||
} else {
|
||||
await Database.Instance.query(`UPDATE UserParty SET UserId = ?, PartyId = ?, CreatedByUserId = ?, CreatedDatetime = ?, LastModifiedByUserId = ?, LastModifiedDatetime = ?, DeletedByUserId = ?, DeletedDatetime = ?, IsDeleted = ?, WHERE Id = ?`, [
|
||||
userParty.UserId, userParty.PartyId, userParty.CreatedByUserId, userParty.CreatedDatetime.getTime(), userParty.LastModifiedByUserId ?? null, userParty.LastModifiedDatetime?.getTime() ?? null, userParty.DeletedByUserId ?? null, userParty.DeletedDatetime?.getTime() ?? null, Number(userParty.IsDeleted), userParty.Id
|
||||
await Database.Instance.query(`UPDATE UserParty SET UserId = ?, PartyId = ?, IsActive = ?, CreatedByUserId = ?, CreatedDatetime = ?, LastModifiedByUserId = ?, LastModifiedDatetime = ?, DeletedByUserId = ?, DeletedDatetime = ?, IsDeleted = ? WHERE Id = ?`, [
|
||||
userParty.UserId, userParty.PartyId, Number(userParty.IsActive), userParty.CreatedByUserId, userParty.CreatedDatetime.getTime(), userParty.LastModifiedByUserId ?? null, userParty.LastModifiedDatetime?.getTime() ?? null, userParty.DeletedByUserId ?? null, userParty.DeletedDatetime?.getTime() ?? null, Number(userParty.IsDeleted), userParty.Id
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
@ -47,6 +75,7 @@ function populateUserPartyFromDB(userParty:UserParty, dbUserParty:any) {
|
|||
userParty.Id = dbUserParty.Id;
|
||||
userParty.UserId = dbUserParty.UserId;
|
||||
userParty.PartyId = dbUserParty.PartyId;
|
||||
userParty.IsActive = dbUserParty.IsActive;
|
||||
userParty.CreatedByUserId = dbUserParty.CreatedByUserId;
|
||||
userParty.CreatedDatetime = new Date(dbUserParty.CreatedDatetime);
|
||||
userParty.LastModifiedByUserId = dbUserParty.LastModifiedByUserId;
|
||||
|
|
|
@ -35,6 +35,15 @@ export default class UserService {
|
|||
}
|
||||
}
|
||||
|
||||
public static async GetUserPartyForUser(userId:number, partyId:number) {
|
||||
try {
|
||||
return await UserPartyRepo.selectByUserIdPartyId(userId, partyId);
|
||||
} catch (e) {
|
||||
Console.printError(`MultiProbe server service error:\n${e}`);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
public static async GetParty(id:number) {
|
||||
try {
|
||||
return await PartyRepo.selectById(id);
|
||||
|
@ -96,4 +105,59 @@ export default class UserService {
|
|||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
public static async AddUserToParty(userId:number, partyId:number) {
|
||||
try {
|
||||
const userParty = new UserParty();
|
||||
userParty.UserId = userId;
|
||||
userParty.PartyId = partyId;
|
||||
userParty.CreatedByUserId = userId;
|
||||
userParty.CreatedDatetime = new Date();
|
||||
|
||||
await UserPartyRepo.insertUpdate(userParty);
|
||||
} catch (e) {
|
||||
Console.printError(`MultiProbe server service error:\n${e}`);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
public static async SetActiveParty(currentUserId:number, partyId:number) {
|
||||
try {
|
||||
await UserPartyRepo.deactivateAll(currentUserId);
|
||||
|
||||
const userParty = await UserPartyRepo.selectByUserIdPartyId(currentUserId, partyId);
|
||||
if (!userParty) {
|
||||
return;
|
||||
}
|
||||
|
||||
userParty.IsActive = true;
|
||||
userParty.LastModifiedByUserId = currentUserId;
|
||||
userParty.LastModifiedDatetime = new Date();
|
||||
|
||||
await UserPartyRepo.insertUpdate(userParty);
|
||||
} catch (e) {
|
||||
Console.printError(`MultiProbe server service error:\n${e}`);
|
||||
console.log(e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
public static async DeactivateCurrentParty(currentUserId:number) {
|
||||
try {
|
||||
await UserPartyRepo.deactivateAll(currentUserId);
|
||||
} catch (e) {
|
||||
Console.printError(`MultiProbe server service error:\n${e}`);
|
||||
console.log(e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
public static async GetActiveParty(currentUserId:number) {
|
||||
try {
|
||||
return await UserPartyRepo.selectActive(currentUserId);
|
||||
} catch (e) {
|
||||
Console.printError(`MultiProbe server service error:\n${e}`);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,8 +5,8 @@
|
|||
<h3>What would you like to do?</h3>
|
||||
<div class="mt-3">
|
||||
<div>
|
||||
<a class="btn btn-primary btn-lg me-2" href="/account/username">Change Username</a>
|
||||
<a class="btn btn-primary btn-lg me-2" href="/account/password">Change Password</a>
|
||||
<a class="btn btn-primary btn-lg me-2 disabled" href="/account/username">Change Username</a>
|
||||
<a class="btn btn-primary btn-lg me-2 disabled" href="/account/password">Change Password</a>
|
||||
</div>
|
||||
<div class="mt-3">
|
||||
<a class="btn btn-primary btn-lg me-2" href="/party/create">Create Party</a>
|
||||
|
@ -31,7 +31,11 @@
|
|||
<td><%= party.Name %></td>
|
||||
<td><%= party.PartyRef %></td>
|
||||
<td class="text-end">
|
||||
<a href="/party/setactive?id=<%= party.Id %>" class="btn btn-sm btn-success me-2">Set Active</a>
|
||||
<% if (activeUserParty && activeUserParty.PartyId === party.Id) { %>
|
||||
<a href="/party/deactivate" class="btn btn-sm btn-success me-2">Deactivate</a>
|
||||
<% } else { %>
|
||||
<a href="/party/setactive?id=<%= party.Id %>" class="btn btn-sm btn-success me-2">Set Current</a>
|
||||
<% } %>
|
||||
<% if (party.CreatedByUserId === user.Id) { %>
|
||||
<a class="btn btn-sm btn-danger disabled" title="You may not leave a party if you created it." onclick="alert('You may not leave a party if you created it.')">Leave</a>
|
||||
<% } else { %>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<%- include("../base/header", { title: typeof(party) === "undefined" ? "Create Party" : `Editing ${party.Name}` }) %>
|
||||
<%- include("../base/header", { title: typeof(party) === "undefined" ? "Create Party" : `Editing ${party.Name}`, userId: session.userId }) %>
|
||||
<% if (typeof(party) === "undefined") { %>
|
||||
<h1 class="text-center mb-5">Create Party</h1>
|
||||
<% } else { %>
|
||||
|
@ -8,13 +8,19 @@
|
|||
<div class="row">
|
||||
<div class="col-4"></div>
|
||||
<div class="col-4">
|
||||
<% if (typeof(error) === "string") { %>
|
||||
<div class="alert alert-danger">
|
||||
<%= error %>
|
||||
</div>
|
||||
<% } %>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="form-label">Party Name</label>
|
||||
<input class="form-control" type="text" name="partyName" maxlength="64" required />
|
||||
<input class="form-control" type="text" name="partyName" maxlength="64" value="<%= typeof(partyName) === "undefined" ? "" : partyName %>" required />
|
||||
</div>
|
||||
<div class="form-group mt-3">
|
||||
<label class="form-label">Join Code / Party ID<br><span style="font-size: 10pt;">Pick something nice, e.g. "<b>3EGGS</b>"</span></label>
|
||||
<input class="form-control" type="text" name="partyRef" minlength="5" maxlength="5" required />
|
||||
<input class="form-control" type="text" name="partyRef" minlength="5" maxlength="5" value="<%= typeof(partyRef) === "undefined" ? "" : partyRef %>" required />
|
||||
</div>
|
||||
|
||||
<div class="text-center mt-5">
|
||||
|
|
|
@ -1,12 +1,18 @@
|
|||
<%- include("../base/header", { title: "Join Party" }) %>
|
||||
<%- include("../base/header", { title: "Join Party", userId: session.userId }) %>
|
||||
<h1 class="text-center mb-5">Join Party</h1>
|
||||
<form method="post">
|
||||
<div class="row">
|
||||
<div class="col-4"></div>
|
||||
<div class="col-4">
|
||||
<% if (typeof(error) === "string") { %>
|
||||
<div class="alert alert-danger">
|
||||
<%= error %>
|
||||
</div>
|
||||
<% } %>
|
||||
|
||||
<div class="form-group mt-3">
|
||||
<label class="form-label">Join Code / Party ID <span style="font-size: 10pt;">e.g. "<b>3EGGS</b>"</span></label>
|
||||
<input class="form-control" type="text" name="partyRef" minlength="5" maxlength="5" required />
|
||||
<input class="form-control" type="text" name="partyRef" minlength="5" maxlength="5" value="<%= typeof(partyRef) === "undefined" ? "" : partyRef %>" required />
|
||||
</div>
|
||||
|
||||
<div class="text-center mt-5">
|
||||
|
|
Loading…
Reference in a new issue