This commit is contained in:
MathMan05
2025-12-13 15:36:02 -06:00
parent 925ec86f4d
commit a9cf43fe89
6 changed files with 119 additions and 36 deletions
+20 -1
View File
@@ -644,6 +644,11 @@ class Localuser {
this.rights.update(rights);
this.perminfo.user.rights = rights;
}
traceSub() {
SW.captureEvent("trace", (e) => {
this.handleTrace(e.trace);
});
}
async handleEvent(temp: wsjson) {
if (temp.d._trace) this.handleTrace(temp.d._trace);
if (localStorage.getItem("logGateway")) console.debug(temp);
@@ -3247,9 +3252,23 @@ class Localuser {
SW.postMessage({code: "isDev", dev: e});
};
devSettings.addText(I18n.devSettings.cacheDesc());
const box5 = devSettings.addCheckboxInput(I18n.devSettings.captureTrace(), () => {}, {
initState: !!localStorage.getItem("capTrace"),
});
box5.onchange = (e) => {
if (e) {
localStorage.setItem("capTrace", "true");
} else {
localStorage.removeItem("capTrace");
}
SW.traceInit();
};
}
if (this.trace.length && localStorage.getItem("traces")) {
const traces = settings.addButton(I18n.localuser.trace());
const traces = settings.addButton(I18n.localuser.trace(), {
noSubmit: true,
});
const traceArr = this.trace;
const sel = traces.addSelect(
+31 -1
View File
@@ -171,8 +171,30 @@ async function getfile(event: FetchEvent): Promise<Response> {
}
}
self.addEventListener("fetch", (e) => {
self.addEventListener("fetch", async (e) => {
const event = e as FetchEvent;
if (URL.canParse(event.request.url) && apiHosts?.has(new URL(event.request.url).host)) {
try {
const responce = await fetch(event.request.clone());
try {
event.respondWith(responce.clone());
} catch {}
const json = await responce.json();
if (json._trace) {
sendAll({
code: "trace",
trace: json._trace,
});
}
} catch (e) {
console.error(e);
//Wasn't meant to be ig lol
}
return;
}
if (event.request.method === "POST") {
return;
}
@@ -187,6 +209,7 @@ self.addEventListener("fetch", (e) => {
});
const ports = new Set<MessagePort>();
let dev = false;
let apiHosts: Set<string> | void;
function listenToPort(port: MessagePort) {
function sendMessage(message: messageFrom) {
port.postMessage(message);
@@ -233,6 +256,13 @@ function listenToPort(port: MessagePort) {
}
break;
}
case "apiUrls": {
if (data.hosts) {
apiHosts = new Set(data.hosts);
} else {
apiHosts = undefined;
}
}
}
};
port.addEventListener("close", () => {
+37 -31
View File
@@ -1112,7 +1112,7 @@ class Options implements OptionsElement<void> {
headers = {},
method = "POST",
traditionalSubmit = false,
tfaCheck=true
tfaCheck = true,
} = {},
) {
const options = new Form(name, this, onSubmit, {
@@ -1122,7 +1122,7 @@ class Options implements OptionsElement<void> {
headers,
method,
traditionalSubmit,
tfaCheck
tfaCheck,
});
this.subOptions = options;
this.genTop();
@@ -1304,10 +1304,10 @@ class Options implements OptionsElement<void> {
container.append(div);
}
}
deleteElm(opt:OptionsElement<any>){
deleteElm(opt: OptionsElement<any>) {
const html = this.html.get(opt)?.deref();
this.options=this.options.filter(_=>_!==opt);
if(!html) return;
this.options = this.options.filter((_) => _ !== opt);
if (!html) return;
html.remove();
this.html.delete(opt);
}
@@ -1559,42 +1559,48 @@ class FormError extends Error {
}
async function handle2fa(json: any, api: string): Promise<false | any> {
if (json.ticket) {
if(json.webauthn){
const challenge = JSON.parse(json.webauthn).publicKey as PublicKeyCredentialRequestOptionsJSON;
challenge.challenge=challenge.challenge.split("=")[0].replaceAll("+","-").replaceAll("/","_");
challenge.allowCredentials?.forEach(_=>_.id=_.id.split("=")[0].replaceAll("+","-").replaceAll("/","_"))
if (json.webauthn) {
const challenge = JSON.parse(json.webauthn)
.publicKey as PublicKeyCredentialRequestOptionsJSON;
challenge.challenge = challenge.challenge
.split("=")[0]
.replaceAll("+", "-")
.replaceAll("/", "_");
challenge.allowCredentials?.forEach(
(_) => (_.id = _.id.split("=")[0].replaceAll("+", "-").replaceAll("/", "_")),
);
console.log(challenge);
const options = PublicKeyCredential.parseRequestOptionsFromJSON(challenge);
const credential = await navigator.credentials.get({publicKey: options}) as unknown as {
rawId:ArrayBuffer,
response:{
[key:string]:ArrayBuffer,
}
const credential = (await navigator.credentials.get({publicKey: options})) as unknown as {
rawId: ArrayBuffer;
response: {
[key: string]: ArrayBuffer;
};
};
if(!credential) return false;
function toBase64(buf:ArrayBuffer){
return btoa(String.fromCharCode(...new Uint8Array(buf)))
if (!credential) return false;
function toBase64(buf: ArrayBuffer) {
return btoa(String.fromCharCode(...new Uint8Array(buf)));
}
const keys = ["authenticatorData","clientDataJSON","signature"];
const keys = ["authenticatorData", "clientDataJSON", "signature"];
const response = {} as any;
for(const key of keys){
response[key]=toBase64(credential.response[key] as ArrayBuffer);
for (const key of keys) {
response[key] = toBase64(credential.response[key] as ArrayBuffer);
}
const res = {
rawId:toBase64(credential.rawId),
response
rawId: toBase64(credential.rawId),
response,
};
const resObj = await fetch(api + "/auth/mfa/webauthn",{
const resObj = await fetch(api + "/auth/mfa/webauthn", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body:JSON.stringify({code:JSON.stringify(res),ticket:json.ticket})
body: JSON.stringify({code: JSON.stringify(res), ticket: json.ticket}),
});
if(!resObj.ok) return false;
if (!resObj.ok) return false;
const jsonRes = await resObj.json();
return jsonRes;
}else{
} else {
return new Promise<boolean>((resolution) => {
const better = new Dialog("");
const form = better.options.addForm(
@@ -1656,7 +1662,7 @@ class Form implements OptionsElement<object> {
value!: object;
traditionalSubmit: boolean;
values: {[key: string]: any} = {};
tfaCheck:boolean;
tfaCheck: boolean;
constructor(
name: string,
owner: Options,
@@ -1669,12 +1675,12 @@ class Form implements OptionsElement<object> {
method = "POST",
traditionalSubmit = false,
vsmaller = false,
tfaCheck=true
tfaCheck = true,
} = {},
) {
this.traditionalSubmit = traditionalSubmit;
this.name = name;
this.tfaCheck=tfaCheck;
this.tfaCheck = tfaCheck;
this.method = method;
this.submitText = submitText;
this.options = new Options(name, this, {ltr, vsmaller});
@@ -2153,8 +2159,8 @@ class Settings extends Buttons {
constructor(name: string) {
super(name);
}
addButton(name: string, {ltr = false, optName = name} = {}): Options {
const options = new Options(optName, this, {ltr});
addButton(name: string, {ltr = false, optName = name, noSubmit = false} = {}): Options {
const options = new Options(optName, this, {ltr, noSubmit});
this.add(name, options);
return options;
}
+8
View File
@@ -18,6 +18,10 @@ export type messageTo =
| {
code: "isDev";
dev: boolean;
}
| {
code: "apiUrls";
hosts?: string[];
};
export type messageFrom =
| {
@@ -38,4 +42,8 @@ export type messageFrom =
code: "isValid";
url: string;
valid: boolean;
}
| {
code: "trace";
trace: string[];
};
+21 -2
View File
@@ -35,10 +35,22 @@ export function setTheme() {
}
export function getBulkUsers() {
const json = getBulkInfo();
apiDoms.clear();
for (const thing in json.users) {
const user = (json.users[thing] = new Specialuser(json.users[thing]));
apiDoms.add(new URL(user.serverurls.api).host);
}
if (localStorage.getItem("capTrace")) {
SW.postMessage({
code: "apiUrls",
hosts: [...apiDoms],
});
} else {
SW.postMessage({
code: "apiUrls",
hosts: undefined,
});
}
return json;
}
export function getBulkInfo() {
@@ -883,9 +895,16 @@ export class SW {
}
});
}
static traceInit() {
getBulkUsers();
}
static needsUpdate = false;
static postMessage(message: messageTo) {
this.port?.postMessage(message);
static async postMessage(message: messageTo) {
if (!("serviceWorker" in navigator)) return;
while (!this.port) {
await new Promise((res) => setTimeout(res, 100));
}
this.port.postMessage(message);
}
static eventListeners = new Map<
messageFrom["code"],
+2 -1
View File
@@ -297,7 +297,8 @@
"logGateway":"Log received gateway events (log level info):",
"traces":"Expose traces:",
"cache":"Enable Service Worker Caching map files:",
"cacheDesc":"map files will still load either way, this'll just make sure they're in cache when a new update rolls out."
"cacheDesc":"map files will still load either way, this'll just make sure they're in cache when a new update rolls out.",
"captureTrace":"This setting tells Fermi to capture _trace properties from the server, enabling this may cause progressive JSON decoding to not work (might require a reload)"
},
"htmlPages": {
"idpermissions": "This will allow the bot to:",