start work on logged in pages + add auth
This commit is contained in:
parent
88e92051e3
commit
1183f0f9b6
17 changed files with 300 additions and 23 deletions
|
@ -3,6 +3,11 @@
|
||||||
"http": 38194,
|
"http": 38194,
|
||||||
"ws": 39195
|
"ws": 39195
|
||||||
},
|
},
|
||||||
|
"session": {
|
||||||
|
"validity": 86400,
|
||||||
|
"length": 64,
|
||||||
|
"secret": "changeme"
|
||||||
|
},
|
||||||
"database": {
|
"database": {
|
||||||
"address": "localhost",
|
"address": "localhost",
|
||||||
"port": 3306,
|
"port": 3306,
|
||||||
|
|
117
server/index.ts
117
server/index.ts
|
@ -1,6 +1,8 @@
|
||||||
import { createReader, createWriter, Endian } from "bufferstuff";
|
import { createReader, createWriter, Endian } from "bufferstuff";
|
||||||
import { WebSocketServer } from "ws";
|
import { WebSocketServer } from "ws";
|
||||||
import Fastify from "fastify";
|
import Fastify from "fastify";
|
||||||
|
import FastifyFormBody from "@fastify/formbody";
|
||||||
|
import FastifyCookie from "@fastify/cookie";
|
||||||
import FastifyView from "@fastify/view";
|
import FastifyView from "@fastify/view";
|
||||||
import EJS from "ejs";
|
import EJS from "ejs";
|
||||||
import Config from "./objects/Config";
|
import Config from "./objects/Config";
|
||||||
|
@ -9,6 +11,11 @@ import RemoteUser from "./objects/RemoteUser";
|
||||||
import { MessageType } from "./enums/MessageType";
|
import { MessageType } from "./enums/MessageType";
|
||||||
import Database from "./objects/Database";
|
import Database from "./objects/Database";
|
||||||
import { Console } from "hsconsole";
|
import { Console } from "hsconsole";
|
||||||
|
import UserService from "./services/UserService";
|
||||||
|
import UsernameData from "./interfaces/UsernameData";
|
||||||
|
import { randomBytes } from "crypto";
|
||||||
|
import SessionUser from "./objects/SessionUser";
|
||||||
|
import PasswordUtility from "./utilities/PasswordUtility";
|
||||||
|
|
||||||
Console.customHeader(`MultiProbe server started at ${new Date()}`);
|
Console.customHeader(`MultiProbe server started at ${new Date()}`);
|
||||||
|
|
||||||
|
@ -18,6 +25,17 @@ new Database(Config.database.address, Config.database.port, Config.database.user
|
||||||
|
|
||||||
// Web stuff
|
// Web stuff
|
||||||
|
|
||||||
|
const sessions = new FunkyArray<string, SessionUser>();
|
||||||
|
const sessionExpiryInterval = setInterval(() => {
|
||||||
|
const currentTime = Date.now();
|
||||||
|
for (const key of sessions.keys) {
|
||||||
|
const session = sessions.get(key);
|
||||||
|
if (!session || (session && currentTime >= session.validityPeriod.getTime())) {
|
||||||
|
sessions.remove(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, 3600000);
|
||||||
|
|
||||||
const fastify = Fastify({
|
const fastify = Fastify({
|
||||||
logger: false
|
logger: false
|
||||||
});
|
});
|
||||||
|
@ -26,9 +44,43 @@ fastify.register(FastifyView, {
|
||||||
engine: {
|
engine: {
|
||||||
ejs: EJS
|
ejs: EJS
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
|
|
||||||
|
fastify.register(FastifyFormBody);
|
||||||
|
|
||||||
|
fastify.register(FastifyCookie, {
|
||||||
|
secret: Config.session.secret,
|
||||||
|
parseOptions: {
|
||||||
|
path: "/",
|
||||||
|
secure: true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Get Methods
|
||||||
|
|
||||||
|
function validateSession(cookies:{ [cookieName: string]: string | undefined }) {
|
||||||
|
if ("MP_SESSION" in cookies && typeof(cookies["MP_SESSION"]) === "string") {
|
||||||
|
const key = FastifyCookie.unsign(cookies["MP_SESSION"], Config.session.secret);
|
||||||
|
if (key.valid && sessions.has(key.value ?? "badkey")) {
|
||||||
|
return sessions.get(key.value ?? "badkey");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
fastify.get("/", async (req, res) => {
|
fastify.get("/", async (req, res) => {
|
||||||
|
let session:SessionUser | undefined;
|
||||||
|
if (session = validateSession(req.cookies)) {
|
||||||
|
const user = await UserService.GetUser(session.userId);
|
||||||
|
//const groups = await UserService.GetUserParties(session.userId);
|
||||||
|
if (user) {
|
||||||
|
return res.view("templates/home.ejs", { user, parties: [] });
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.view("templates/index.ejs", { });
|
||||||
|
}
|
||||||
|
|
||||||
return res.view("templates/index.ejs", { });
|
return res.view("templates/index.ejs", { });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -45,7 +97,66 @@ fastify.get("/account/register", async (req, res) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
fastify.setNotFoundHandler(async (req, res) => {
|
fastify.setNotFoundHandler(async (req, res) => {
|
||||||
return res.view("templates/404.ejs", { });
|
return res.status(404).view("templates/404.ejs", { });
|
||||||
|
});
|
||||||
|
|
||||||
|
// Post Methods
|
||||||
|
|
||||||
|
fastify.post("/account/register", async (req, res) => {
|
||||||
|
const data = req.body as UsernameData;
|
||||||
|
if (typeof(data.username) !== "string" || typeof(data.password) !== "string" || data.username.length > 32 || data.password.length < 8) {
|
||||||
|
return res.view("templates/account/register.ejs", { });
|
||||||
|
}
|
||||||
|
|
||||||
|
const username = data.username.replaceAll("<", "<").replaceAll(">", ">");
|
||||||
|
await UserService.CreateUser(0, username, data.password);
|
||||||
|
|
||||||
|
const user = await UserService.GetUserByUsername(username);
|
||||||
|
if (!user) {
|
||||||
|
return res.view("templates/account/register.ejs", { });
|
||||||
|
}
|
||||||
|
|
||||||
|
const validPeriod = new Date();
|
||||||
|
validPeriod.setTime(validPeriod.getTime() + Config.session.validity);
|
||||||
|
const key = randomBytes(Config.session.length).toString("hex");
|
||||||
|
|
||||||
|
sessions.set(key, new SessionUser(user.Id, validPeriod));
|
||||||
|
|
||||||
|
res.setCookie("MP_SESSION", key, {
|
||||||
|
path: "/",
|
||||||
|
signed: true
|
||||||
|
});
|
||||||
|
|
||||||
|
return res.redirect(302, "/");
|
||||||
|
});
|
||||||
|
|
||||||
|
fastify.post("/account/login", async (req, res) => {
|
||||||
|
const data = req.body as UsernameData;
|
||||||
|
if (typeof(data.username) !== "string" || typeof(data.password) !== "string" || data.username.length > 32 || data.password.length < 8) {
|
||||||
|
return res.view("templates/account/login.ejs", { });
|
||||||
|
}
|
||||||
|
|
||||||
|
const user = await UserService.GetUserByUsername(data.username);
|
||||||
|
if (!user) {
|
||||||
|
return res.view("templates/account/login.ejs", { });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (await PasswordUtility.ValidatePassword(user.PasswordHash, user.PasswordSalt, data.password)) {
|
||||||
|
const validPeriod = new Date();
|
||||||
|
validPeriod.setTime(validPeriod.getTime() + Config.session.validity);
|
||||||
|
const key = randomBytes(Config.session.length).toString("hex");
|
||||||
|
|
||||||
|
sessions.set(key, new SessionUser(user.Id, validPeriod));
|
||||||
|
|
||||||
|
res.setCookie("MP_SESSION", key, {
|
||||||
|
path: "/",
|
||||||
|
signed: true
|
||||||
|
});
|
||||||
|
|
||||||
|
return res.redirect(302, "/");
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.view("templates/account/login.ejs", { });
|
||||||
});
|
});
|
||||||
|
|
||||||
// Websocket stuff
|
// Websocket stuff
|
||||||
|
@ -175,6 +286,8 @@ function shutdown() {
|
||||||
Console.printInfo("Shutting down...");
|
Console.printInfo("Shutting down...");
|
||||||
websocketServer.close(async () => {
|
websocketServer.close(async () => {
|
||||||
await fastify.close();
|
await fastify.close();
|
||||||
|
clearInterval(sessionExpiryInterval);
|
||||||
|
|
||||||
Console.cleanup();
|
Console.cleanup();
|
||||||
console.log("Goodbye!");
|
console.log("Goodbye!");
|
||||||
});
|
});
|
||||||
|
|
4
server/interfaces/UsernameData.ts
Normal file
4
server/interfaces/UsernameData.ts
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
export default interface UsernameData {
|
||||||
|
username?: string,
|
||||||
|
password?: string
|
||||||
|
}
|
|
@ -6,6 +6,7 @@ export default class Config {
|
||||||
public constructor() { throw new Error("Static Class"); }
|
public constructor() { throw new Error("Static Class"); }
|
||||||
|
|
||||||
public static ports:ConfigPorts = config.ports;
|
public static ports:ConfigPorts = config.ports;
|
||||||
|
public static session:ConfigSession = config.session;
|
||||||
public static database:ConfigDatabase = config.database;
|
public static database:ConfigDatabase = config.database;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,6 +15,12 @@ interface ConfigPorts {
|
||||||
ws: number
|
ws: number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface ConfigSession {
|
||||||
|
validity: number,
|
||||||
|
length: number,
|
||||||
|
secret: string
|
||||||
|
}
|
||||||
|
|
||||||
interface ConfigDatabase {
|
interface ConfigDatabase {
|
||||||
address: string,
|
address: string,
|
||||||
port: number,
|
port: number,
|
||||||
|
|
9
server/objects/SessionUser.ts
Normal file
9
server/objects/SessionUser.ts
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
export default class SessionUser {
|
||||||
|
public readonly userId:number;
|
||||||
|
public readonly validityPeriod:Date;
|
||||||
|
|
||||||
|
constructor(userId:number, validityPeriod:Date) {
|
||||||
|
this.userId = userId;
|
||||||
|
this.validityPeriod = validityPeriod;
|
||||||
|
}
|
||||||
|
}
|
28
server/package-lock.json
generated
28
server/package-lock.json
generated
|
@ -9,6 +9,8 @@
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@fastify/cookie": "^9.3.1",
|
||||||
|
"@fastify/formbody": "^7.4.0",
|
||||||
"@fastify/view": "^9.0.0",
|
"@fastify/view": "^9.0.0",
|
||||||
"bufferstuff": "^1.5.1",
|
"bufferstuff": "^1.5.1",
|
||||||
"ejs": "^3.1.10",
|
"ejs": "^3.1.10",
|
||||||
|
@ -52,6 +54,15 @@
|
||||||
"fast-uri": "^2.0.0"
|
"fast-uri": "^2.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@fastify/cookie": {
|
||||||
|
"version": "9.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@fastify/cookie/-/cookie-9.3.1.tgz",
|
||||||
|
"integrity": "sha512-h1NAEhB266+ZbZ0e9qUE6NnNR07i7DnNXWG9VbbZ8uC6O/hxHpl+Zoe5sw1yfdZ2U6XhToUGDnzQtWJdCaPwfg==",
|
||||||
|
"dependencies": {
|
||||||
|
"cookie-signature": "^1.1.0",
|
||||||
|
"fastify-plugin": "^4.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@fastify/error": {
|
"node_modules/@fastify/error": {
|
||||||
"version": "3.4.1",
|
"version": "3.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/@fastify/error/-/error-3.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/@fastify/error/-/error-3.4.1.tgz",
|
||||||
|
@ -65,6 +76,15 @@
|
||||||
"fast-json-stringify": "^5.7.0"
|
"fast-json-stringify": "^5.7.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@fastify/formbody": {
|
||||||
|
"version": "7.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@fastify/formbody/-/formbody-7.4.0.tgz",
|
||||||
|
"integrity": "sha512-H3C6h1GN56/SMrZS8N2vCT2cZr7mIHzBHzOBa5OPpjfB/D6FzP9mMpE02ZzrFX0ANeh0BAJdoXKOF2e7IbV+Og==",
|
||||||
|
"dependencies": {
|
||||||
|
"fast-querystring": "^1.0.0",
|
||||||
|
"fastify-plugin": "^4.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@fastify/merge-json-schemas": {
|
"node_modules/@fastify/merge-json-schemas": {
|
||||||
"version": "0.1.1",
|
"version": "0.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/@fastify/merge-json-schemas/-/merge-json-schemas-0.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/@fastify/merge-json-schemas/-/merge-json-schemas-0.1.1.tgz",
|
||||||
|
@ -598,6 +618,14 @@
|
||||||
"node": ">= 0.6"
|
"node": ">= 0.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/cookie-signature": {
|
||||||
|
"version": "1.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.1.tgz",
|
||||||
|
"integrity": "sha512-78KWk9T26NhzXtuL26cIJ8/qNHANyJ/ZYrmEXFzUmhZdjpBv+DlWlOANRTGBt48YcyslsLrj0bMLFTmXvLRCOw==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.6.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/create-require": {
|
"node_modules/create-require": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
|
||||||
|
|
|
@ -15,6 +15,8 @@
|
||||||
"author": "tgpholly",
|
"author": "tgpholly",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@fastify/cookie": "^9.3.1",
|
||||||
|
"@fastify/formbody": "^7.4.0",
|
||||||
"@fastify/view": "^9.0.0",
|
"@fastify/view": "^9.0.0",
|
||||||
"bufferstuff": "^1.5.1",
|
"bufferstuff": "^1.5.1",
|
||||||
"ejs": "^3.1.10",
|
"ejs": "^3.1.10",
|
||||||
|
|
|
@ -29,11 +29,11 @@ export default class PartyRepo {
|
||||||
public static async insertUpdate(user:User) {
|
public static async insertUpdate(user:User) {
|
||||||
if (user.Id === Number.MIN_VALUE) {
|
if (user.Id === Number.MIN_VALUE) {
|
||||||
await Database.Instance.query("INSERT Party (Username, PasswordHash, PasswordSalt, CreatedByUserId, CreatedDatetime, LastModifiedByUserId, LastModifiedDatetime, DeletedByUserId, DeletedDatetime, IsDeleted) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", [
|
await Database.Instance.query("INSERT Party (Username, PasswordHash, PasswordSalt, CreatedByUserId, CreatedDatetime, LastModifiedByUserId, LastModifiedDatetime, DeletedByUserId, DeletedDatetime, IsDeleted) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", [
|
||||||
user.Username, user.PasswordHash, user.PasswordSalt, user.CreatedByUserId, user.CreatedDatetime.getTime(), user.LastModifiedByUserId, user.LastModifiedDatetime?.getTime(), user.DeletedByUserId, user.DeletedDatetime?.getTime(), Number(user.IsDeleted)
|
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)
|
||||||
]);
|
]);
|
||||||
} else {
|
} else {
|
||||||
await Database.Instance.query(`UPDATE Party SET Username = ?, PasswordHash = ?, PasswordSalt = ?, CreatedByUserId = ?, CreatedDatetime = ?, LastModifiedByUserId = ?, LastModifiedDatetime = ?, DeletedByUserId = ?, DeletedDatetime = ?, IsDeleted = ?, WHERE Id = ?`, [
|
await Database.Instance.query(`UPDATE Party SET Username = ?, PasswordHash = ?, PasswordSalt = ?, CreatedByUserId = ?, CreatedDatetime = ?, LastModifiedByUserId = ?, LastModifiedDatetime = ?, DeletedByUserId = ?, DeletedDatetime = ?, IsDeleted = ?, WHERE Id = ?`, [
|
||||||
user.Username, user.PasswordHash, user.PasswordSalt, user.CreatedByUserId, user.CreatedDatetime.getTime(), user.LastModifiedByUserId, user.LastModifiedDatetime?.getTime(), user.DeletedByUserId, user.DeletedDatetime?.getTime(), Number(user.IsDeleted), user.Id
|
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.Id
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,11 +28,11 @@ export default class UserRepo {
|
||||||
public static async insertUpdate(user:User) {
|
public static async insertUpdate(user:User) {
|
||||||
if (user.Id === Number.MIN_VALUE) {
|
if (user.Id === Number.MIN_VALUE) {
|
||||||
await Database.Instance.query("INSERT User (Username, PasswordHash, PasswordSalt, CreatedByUserId, CreatedDatetime, LastModifiedByUserId, LastModifiedDatetime, DeletedByUserId, DeletedDatetime, IsDeleted) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", [
|
await Database.Instance.query("INSERT User (Username, PasswordHash, PasswordSalt, CreatedByUserId, CreatedDatetime, LastModifiedByUserId, LastModifiedDatetime, DeletedByUserId, DeletedDatetime, IsDeleted) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", [
|
||||||
user.Username, user.PasswordHash, user.PasswordSalt, user.CreatedByUserId, user.CreatedDatetime.getTime(), user.LastModifiedByUserId, user.LastModifiedDatetime?.getTime(), user.DeletedByUserId, user.DeletedDatetime?.getTime(), Number(user.IsDeleted)
|
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)
|
||||||
]);
|
]);
|
||||||
} else {
|
} else {
|
||||||
await Database.Instance.query(`UPDATE User SET Username = ?, PasswordHash = ?, PasswordSalt = ?, CreatedByUserId = ?, CreatedDatetime = ?, LastModifiedByUserId = ?, LastModifiedDatetime = ?, DeletedByUserId = ?, DeletedDatetime = ?, IsDeleted = ?, WHERE Id = ?`, [
|
await Database.Instance.query(`UPDATE User SET Username = ?, PasswordHash = ?, PasswordSalt = ?, CreatedByUserId = ?, CreatedDatetime = ?, LastModifiedByUserId = ?, LastModifiedDatetime = ?, DeletedByUserId = ?, DeletedDatetime = ?, IsDeleted = ?, WHERE Id = ?`, [
|
||||||
user.Username, user.PasswordHash, user.PasswordSalt, user.CreatedByUserId, user.CreatedDatetime.getTime(), user.LastModifiedByUserId, user.LastModifiedDatetime?.getTime(), user.DeletedByUserId, user.DeletedDatetime?.getTime(), Number(user.IsDeleted), user.Id
|
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.Id
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,14 @@ export default class UserService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static async GetUserByUsername(username:string) {
|
||||||
|
try {
|
||||||
|
return await UserRepo.selectByUsername(username);
|
||||||
|
} catch (e) {
|
||||||
|
Console.printError(`MultiProbe server service error:\n${e}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static async GetParty(id:number) {
|
public static async GetParty(id:number) {
|
||||||
try {
|
try {
|
||||||
return await PartyRepo.selectById(id);
|
return await PartyRepo.selectById(id);
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
<%- include("base/header") -%>
|
<%- include("base/header", { title: "404"}) %>
|
||||||
<h1 class="text-center" style="font-size:10rem">404!</h1>
|
<h1 class="text-center" style="font-size:10rem">404!</h1>
|
||||||
<%- include("base/footer") -%>
|
<%- include("base/footer") %>
|
|
@ -1,22 +1,27 @@
|
||||||
<%- include("../base/header") -%>
|
<%- include("../base/header", { title: "Login" }) %>
|
||||||
<h1 class="text-center mb-5">Login</h1>
|
<h1 class="text-center mb-5">Login</h1>
|
||||||
<form>
|
<form method="post">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-4"></div>
|
<div class="col-4"></div>
|
||||||
<div class="col-4">
|
<div class="col-4">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label">Username</label>
|
<label class="form-label">Username</label>
|
||||||
<input class="form-control" type="text" name="u" maxlength="32" required />
|
<input class="form-control" type="text" name="username" maxlength="32" required />
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group mt-3">
|
<div class="form-group mt-3">
|
||||||
<label class="form-label">Password</label>
|
<label class="form-label">Password</label>
|
||||||
<input class="form-control" type="password" name="p" required />
|
<input class="form-control" type="password" name="password" required />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="text-center mt-3">
|
<div class="text-center mt-3">
|
||||||
<input class="btn btn-primary" type="submit" value="Login" />
|
<a href="/account/register">I don't have an account.</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="text-center mt-3">
|
||||||
|
<input class="btn btn-primary" type="submit" value="Continue" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-4"></div>
|
<div class="col-4"></div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
<%- include("../base/footer") -%>
|
<%- include("../base/footer") %>
|
27
server/templates/account/register.ejs
Normal file
27
server/templates/account/register.ejs
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
<%- include("../base/header", { title: "Register" }) %>
|
||||||
|
<h1 class="text-center mb-5">Register</h1>
|
||||||
|
<form method="post">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-4"></div>
|
||||||
|
<div class="col-4">
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label">Username</label>
|
||||||
|
<input class="form-control" type="text" name="username" maxlength="32" required />
|
||||||
|
</div>
|
||||||
|
<div class="form-group mt-3">
|
||||||
|
<label class="form-label">Password</label>
|
||||||
|
<input class="form-control" type="password" name="password" minlength="8" autocomplete="new-password" required />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="text-center mt-3">
|
||||||
|
<a href="/account/login">I already have an account!</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="text-center mt-3">
|
||||||
|
<input class="btn btn-primary" type="submit" value="Continue" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-4"></div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<%- include("../base/footer") %>
|
|
@ -1,4 +1,22 @@
|
||||||
</div>
|
</div>
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.3/js/bootstrap.min.js" integrity="sha512-ykZ1QQr0Jy/4ZkvKuqWn4iF3lqPZyij9iRv6sGqLRdTPkY69YX6+7wvVGmsdBbiIfN/8OdsI7HABjvEok6ZopQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.3/js/bootstrap.min.js" integrity="sha512-ykZ1QQr0Jy/4ZkvKuqWn4iF3lqPZyij9iRv6sGqLRdTPkY69YX6+7wvVGmsdBbiIfN/8OdsI7HABjvEok6ZopQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/cookieconsent/3.1.1/cookieconsent.min.js" integrity="sha512-yXXqOFjdjHNH1GND+1EO0jbvvebABpzGKD66djnUfiKlYME5HGMUJHoCaeE4D5PTG2YsSJf6dwqyUUvQvS0vaA==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
||||||
|
<script>
|
||||||
|
window.cookieconsent.initialise({
|
||||||
|
"palette": {
|
||||||
|
"popup": {
|
||||||
|
"background": "#0b5ed7",
|
||||||
|
"text": "#fff"
|
||||||
|
},
|
||||||
|
"button": {
|
||||||
|
"background": "#198754",
|
||||||
|
"text": "#fff"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"content": {
|
||||||
|
"message": "This site uses cookies to retain your login, no more, no less."
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
|
@ -3,16 +3,19 @@
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>Document</title>
|
<title><%= title %> - MultiProbe</title>
|
||||||
|
|
||||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.3/css/bootstrap.min.css" integrity="sha512-jnSuA4Ss2PkkikSOLtYs8BlYIeeIK1h99ty4YfvRPAlzr377vr3CXDb7sb7eEEBYjDtcYj+AjBH3FLv5uSJuXg==" crossorigin="anonymous" referrerpolicy="no-referrer" />
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.3/css/bootstrap.min.css" integrity="sha512-jnSuA4Ss2PkkikSOLtYs8BlYIeeIK1h99ty4YfvRPAlzr377vr3CXDb7sb7eEEBYjDtcYj+AjBH3FLv5uSJuXg==" crossorigin="anonymous" referrerpolicy="no-referrer" />
|
||||||
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/cookieconsent/3.1.1/cookieconsent.min.css" integrity="sha512-LQ97camar/lOliT/MqjcQs5kWgy6Qz/cCRzzRzUCfv0fotsCTC9ZHXaPQmJV8Xu/PVALfJZ7BDezl5lW3/qBxg==" crossorigin="anonymous" referrerpolicy="no-referrer" />
|
||||||
|
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js" integrity="sha512-v2CJ7UaYy4JwqLDIrZUI/4hqeoQieOmAZNXBeQyjo21dadnwR+8ZaIJVT8EE2iyI61OV8e6M8PP2/4hpQINQ/g==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js" integrity="sha512-v2CJ7UaYy4JwqLDIrZUI/4hqeoQieOmAZNXBeQyjo21dadnwR+8ZaIJVT8EE2iyI61OV8e6M8PP2/4hpQINQ/g==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<nav class="navbar navbar-expand-lg bg-body-tertiary">
|
<nav class="navbar navbar-expand-lg bg-body-tertiary">
|
||||||
<div class="container-fluid">
|
<div class="container-fluid">
|
||||||
<a class="navbar-brand" href="/">MultiProbe</a>
|
<a class="navbar-brand" href="/">
|
||||||
|
MultiProbe
|
||||||
|
</a>
|
||||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
|
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
|
||||||
<span class="navbar-toggler-icon"></span>
|
<span class="navbar-toggler-icon"></span>
|
||||||
</button>
|
</button>
|
||||||
|
@ -20,7 +23,20 @@
|
||||||
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
|
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
|
||||||
|
|
||||||
</ul>
|
</ul>
|
||||||
<a class="nav-link active" aria-current="page" href="/account/login">Login</a>
|
<ul class="navbar-nav">
|
||||||
|
<% if (typeof(userId) !== "undefined") { %>
|
||||||
|
<div class="nav-item float-end">
|
||||||
|
<a class="nav-link" href="/account/logout">Logout</a>
|
||||||
|
</div>
|
||||||
|
<% } else { %>
|
||||||
|
<div class="nav-item float-end">
|
||||||
|
<a class="nav-link" href="/account/login">Login</a>
|
||||||
|
</div>
|
||||||
|
<div class="nav-item float-end">
|
||||||
|
<a class="nav-link" href="/account/register">Register</a>
|
||||||
|
</div>
|
||||||
|
<% } %>
|
||||||
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
|
@ -1,3 +1,33 @@
|
||||||
<%- include("base/header") -%>
|
<%- include("base/header", { title: "Home", userId: user.Id }) %>
|
||||||
<h1>Welcome back USER!</h1>
|
<div class="row">
|
||||||
<%- include("base/footer") -%>
|
<div class="col">
|
||||||
|
<h1>Welcome back <%= user.Username %>!</h1>
|
||||||
|
<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>
|
||||||
|
</div>
|
||||||
|
<div class="mt-3">
|
||||||
|
<a class="btn btn-primary btn-lg me-2" href="/account/password">Create Party</a>
|
||||||
|
<a class="btn btn-primary btn-lg me-2" href="/account/username">Join Party</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<h3>Your Parties</h3>
|
||||||
|
<% if (parties.length > 0) { %>
|
||||||
|
<table class="table table-striped">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th scope="col">Name</th>
|
||||||
|
<th scope="col"></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
</table>
|
||||||
|
<% } else { %>
|
||||||
|
<div class="alert alert-primary" role="alert">You are not in any parties.</div>
|
||||||
|
<% } %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<%- include("base/footer") %>
|
|
@ -1,3 +1,8 @@
|
||||||
<%- include("base/header") -%>
|
<%- include("base/header", { title: "Home" }) %>
|
||||||
<h1>Hello world!</h1>
|
<h1><b>MultiProbe</b></h1>
|
||||||
<%- include("base/footer") -%>
|
<h3>A way to explore <a href="https://angusnicneven.com">Terminal 00</a> with friends.</h3>
|
||||||
|
<div class="mt-3">
|
||||||
|
<a type="button" class="btn btn-primary btn-lg me-2" href="/account/register">Register</a>
|
||||||
|
<a type="button" class="btn btn-secondary btn-lg" href="/account/login">Login</a>
|
||||||
|
</div>
|
||||||
|
<%- include("base/footer") %>
|
Loading…
Reference in a new issue