add multiprobe to the multiprobe site :)

This commit is contained in:
Holly Stubbs 2024-10-13 14:13:33 +01:00
parent 0787dfda47
commit 1b99440d70
Signed by: tgpholly
GPG key ID: B8583C4B7D18119E
22 changed files with 83 additions and 19 deletions

View file

@ -5,7 +5,7 @@
"metrics": 9100 "metrics": 9100
}, },
"session": { "session": {
"validity": 86400, "validity": 86400000,
"length": 64, "length": 64,
"secret": "changeme" "secret": "changeme"
}, },

View file

@ -5,6 +5,7 @@ import Controller from "./Controller";
// for Git server lookup // for Git server lookup
let cachedVersion = ""; let cachedVersion = "";
let cachedClient = "";
let cacheExpiry = 0; let cacheExpiry = 0;
export default class ApiController extends Controller { export default class ApiController extends Controller {
@ -31,7 +32,7 @@ export default class ApiController extends Controller {
} else { } else {
const response = await fetch(`http://${Config.githost}/tgpholly/t00-multiuser/raw/branch/master/client/Terminal-00-Multiuser.user.js?${Date.now()}`); const response = await fetch(`http://${Config.githost}/tgpholly/t00-multiuser/raw/branch/master/client/Terminal-00-Multiuser.user.js?${Date.now()}`);
if (response.status === 200) { if (response.status === 200) {
const content = await response.text(); const content = cachedClient = await response.text();
if (content.includes("@version")) { if (content.includes("@version")) {
cachedVersion = content.split("@version")[1].split("\n")[0].trim().split(".").join(""); cachedVersion = content.split("@version")[1].split("\n")[0].trim().split(".").join("");
cacheExpiry = Date.now() + 30000; cacheExpiry = Date.now() + 30000;
@ -44,4 +45,18 @@ export default class ApiController extends Controller {
} }
} }
} }
public async LatestClient_Get() {
if (Date.now() < cacheExpiry) {
this.ok(cachedClient);
} else {
const response = await fetch(`http://${Config.githost}/tgpholly/t00-multiuser/raw/branch/master/client/Terminal-00-Multiuser.user.js?${Date.now()}`);
if (response.status === 200) {
cachedClient = await response.text();
return this.ok(cachedClient);
} else {
return this.badRequest();
}
}
}
} }

View file

@ -66,6 +66,17 @@ export default abstract class Controller {
} }
} }
res.header("X-Powered-By", "MultiProbe");
if (controllerName !== "api") {
res.header("Strict-Transport-Security", "max-age=31536000; includeSubDomains");
res.header("X-XSS-Protection", "1; mode=block");
res.header("Permissions-Policy", "microphone=(), geolocation=(), magnetometer=(), camera=(), payment=(), usb=(), accelerometer=(), gyroscope=()");
res.header("Referrer-Policy", "strict-origin-when-cross-origin");
res.header("Content-Security-Policy", "block-all-mixed-content;frame-ancestors 'self'");
res.header("X-Frame-Options", "SAMEORIGIN");
res.header("X-Content-Type-Options", "nosniff");
}
const requestCtx = new RequestCtx(req, res, controllerName, methodName, session); const requestCtx = new RequestCtx(req, res, controllerName, methodName, session);
controllerRequestHandler.bind(requestCtx)(req.method === "GET" ? req.query : req.body); controllerRequestHandler.bind(requestCtx)(req.method === "GET" ? req.query : req.body);
} }

View file

@ -120,6 +120,13 @@ fastify.register(FastifyStatic, {
}); });
fastify.setNotFoundHandler(async (req, res) => { fastify.setNotFoundHandler(async (req, res) => {
res.header("Strict-Transport-Security", "max-age=31536000; includeSubDomains");
res.header("X-XSS-Protection", "1; mode=block");
res.header("Permissions-Policy", "microphone=(), geolocation=(), magnetometer=(), camera=(), payment=(), usb=(), accelerometer=(), gyroscope=()");
res.header("Referrer-Policy", "strict-origin-when-cross-origin");
res.header("Content-Security-Policy", "block-all-mixed-content;frame-ancestors 'self'");
res.header("X-Frame-Options", "SAMEORIGIN");
res.header("X-Content-Type-Options", "nosniff");
return res.status(404).view("views/404.ejs", { }); return res.status(404).view("views/404.ejs", { });
}); });

View file

@ -37,7 +37,7 @@ export default abstract class Session {
validPeriod.setTime(validPeriod.getTime() + Config.session.validity); validPeriod.setTime(validPeriod.getTime() + Config.session.validity);
const key = randomBytes(Config.session.length).toString("hex"); const key = randomBytes(Config.session.length).toString("hex");
Session.Sessions.set(key, new SessionUser(user.Id, user.Username, user.UserLevel, validPeriod)); Session.Sessions.set(key, new SessionUser(user.Id, user.Username, user.UserLevel, user.APIKey, validPeriod));
res.setCookie("MP_SESSION", key, { res.setCookie("MP_SESSION", key, {
path: "/", path: "/",

View file

@ -4,12 +4,14 @@ export default class SessionUser {
public readonly userId:number; public readonly userId:number;
public readonly username:string; public readonly username:string;
public readonly userLevel:UserLevel; public readonly userLevel:UserLevel;
public readonly apiKey:string;
public readonly validityPeriod:Date; public readonly validityPeriod:Date;
constructor(userId:number, username:string, userLevel:UserLevel, validityPeriod:Date) { constructor(userId:number, username:string, userLevel:UserLevel, apiKey:string, validityPeriod:Date) {
this.userId = userId; this.userId = userId;
this.username = username; this.username = username;
this.userLevel = userLevel; this.userLevel = userLevel;
this.apiKey = apiKey;
this.validityPeriod = validityPeriod; this.validityPeriod = validityPeriod;
} }
} }

View file

@ -22,4 +22,4 @@
</div> </div>
</div> </div>
<%- include("../base/footer") %> <%- include("../base/footer", { apiKey: session.apiKey, username: session.username }) %>

View file

@ -76,4 +76,4 @@
}); });
</script> </script>
<%- include("../base/footer") %> <%- include("../base/footer", { apiKey: session.apiKey, username: session.username }) %>

View file

@ -46,4 +46,4 @@
</div> </div>
</div> </div>
<%- include("../base/footer") %> <%- include("../base/footer", { apiKey: session.apiKey, username: session.username }) %>

View file

@ -53,4 +53,4 @@
<a class="btn btn-primary btn-lg me-2 mb-3" href="/admin/wssessions">Websocket Sessions</a> <a class="btn btn-primary btn-lg me-2 mb-3" href="/admin/wssessions">Websocket Sessions</a>
</div> </div>
</div> </div>
<%- include("../base/footer") %> <%- include("../base/footer", { apiKey: session.apiKey, username: session.username }) %>

View file

@ -45,4 +45,4 @@
</table> </table>
</div> </div>
</div> </div>
<%- include("../base/footer") %> <%- include("../base/footer", { apiKey: session.apiKey, username: session.username }) %>

View file

@ -40,4 +40,4 @@
</div> </div>
</form> </form>
<%- include("../base/footer") %> <%- include("../base/footer", { apiKey: session.apiKey, username: session.username }) %>

View file

@ -51,4 +51,4 @@
</div> </div>
</form> </form>
<%- include("../base/footer") %> <%- include("../base/footer", { apiKey: session.apiKey, username: session.username }) %>

View file

@ -58,4 +58,4 @@
</div> </div>
</div> </div>
<%- include("../base/footer") %> <%- include("../base/footer", { apiKey: session.apiKey, username: session.username }) %>

View file

@ -45,4 +45,4 @@
</table> </table>
</div> </div>
</div> </div>
<%- include("../base/footer") %> <%- include("../base/footer", { apiKey: session.apiKey, username: session.username }) %>

View file

@ -42,4 +42,4 @@
</table> </table>
</div> </div>
</div> </div>
<%- include("../base/footer") %> <%- include("../base/footer", { apiKey: session.apiKey, username: session.username }) %>

View file

@ -40,4 +40,4 @@
</table> </table>
</div> </div>
</div> </div>
<%- include("../base/footer") %> <%- include("../base/footer", { apiKey: session.apiKey, username: session.username }) %>

View file

@ -1,4 +1,33 @@
</div> </div>
<% if (typeof(apiKey) !== "undefined" && typeof(username) !== "undefined") { %>
<style>
html, body {
cursor: unset!important;
}
html mp_button, html mp_container, html mp_party {
display: none;
}
html mp_cursor mp_container {
display: unset!important;
}
</style>
<script>
localStorage["t00mp_username"] = `<%= username %>`;
localStorage.mpconnectonload = "true";
localStorage.mpshowfirsttime = "false";
localStorage.mpapikey = `<%= apiKey %>`;
</script>
<script src="/api/latestclient?<%= Date.now() %>"></script>
<% } else { %>
<script>
localStorage["t00mp_username"] = "";
localStorage.mpconnectonload = "true";
localStorage.mpshowfirsttime = "false";
localStorage.mpapikey = "";
</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 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> <script>
(() => { (() => {

View file

@ -87,4 +87,4 @@
const popoverTriggerList = document.querySelectorAll('[data-bs-toggle="popover"]'); const popoverTriggerList = document.querySelectorAll('[data-bs-toggle="popover"]');
const popoverList = [...popoverTriggerList].map(popoverTriggerEl => new bootstrap.Popover(popoverTriggerEl, { html: true })); const popoverList = [...popoverTriggerList].map(popoverTriggerEl => new bootstrap.Popover(popoverTriggerEl, { html: true }));
</script> </script>
<%- include("../base/footer") %> <%- include("../base/footer", { apiKey: session.apiKey, username: session.username }) %>

View file

@ -29,4 +29,4 @@
</div> </div>
</div> </div>
</div> </div>
<%- include("../base/footer") %> <%- include("../base/footer", { apiKey: session.apiKey, username: session.username }) %>

View file

@ -21,4 +21,4 @@
</div> </div>
</div> </div>
</div> </div>
<%- include("../base/footer") %> <%- include("../base/footer", { apiKey: session.apiKey, username: session.username }) %>