From c6cf8576ecf3c898f65e7d22764592e8f4919e8b Mon Sep 17 00:00:00 2001 From: "[it/its]@Rory&" <[it/its]@Rory&> Date: Tue, 30 Dec 2025 21:51:55 +0100 Subject: [PATCH 01/10] Well known v2 support This patchset contains support for .well-known/spacebar/client, as well as various typo fixes and some minor cleanups. Kind regards, Emma [it/its] @ Rory& Emma [it/its]@Rory& (7): src/utils: Well-known v2 support typo fix: instace -> instance typo fix: responce -> response Mathium, fix your keyboard lol It works! Let's gooooo!~ Typo fix: protical -> protocol Typo fix: orgin -> origin src/index.ts | 4 +- src/utils.ts | 96 +++++++++--- src/webpage/instances.json | 2 +- src/webpage/localuser.ts | 8 +- src/webpage/login.ts | 4 +- src/webpage/markdown.ts | 4 +- src/webpage/register.ts | 4 +- src/webpage/service.ts | 16 +- src/webpage/settings.ts | 18 +-- src/webpage/utils/netUtils.ts | 8 + src/webpage/utils/utils.ts | 287 +++++++++++++++++----------------- 11 files changed, 260 insertions(+), 191 deletions(-) create mode 100644 src/webpage/utils/netUtils.ts -- 2.51.2 From a00435c9f65551bd50bede1cb2aea871322b4683 Mon Sep 17 00:00:00 2001 From: "[it/its]@Rory&" <[it/its]@Rory&> Date: Tue, 30 Dec 2025 14:34:32 +0100 Subject: [PATCH 02/10] src/utils: Well-known v2 support --- src/utils.ts | 92 ++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 75 insertions(+), 17 deletions(-) diff --git a/src/utils.ts b/src/utils.ts index 8e9b8507..4ef25665 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,4 +1,5 @@ import {instace} from "./index.js"; + interface ApiUrls { api: string; gateway: string; @@ -14,10 +15,11 @@ export async function getApiUrls( if (!url.endsWith("/")) { url += "/"; } + if (check) { let valid = false; - for (const instace of instances) { - const urlstr = instace.url || instace.urls?.api; + for (const instance of instances) { + const urlstr = instance.url || instance.urls?.api; if (!urlstr) { continue; } @@ -34,21 +36,77 @@ export async function getApiUrls( throw new Error("Invalid instance"); } } + + const hostName = new URL(url).hostname; try { - const info: ApiUrls = await fetch(`${url}.well-known/spacebar`).then((res) => res.json()); - const api = info.api; - const apiUrl = new URL(api); - const policies: any = await fetch( - `${api}${apiUrl.pathname.includes("api") ? "" : "api"}/policies/instance/domains`, - ).then((res) => res.json()); - return { - api: policies.apiEndpoint, - gateway: policies.gateway, - cdn: policies.cdn, - wellknown: url, - }; - } catch (error) { - console.error("Error fetching API URLs:", error); - return null; + return await getApiUrlsV2(url); + } catch (e) { + console.warn( + `[WARN] Failed to get V2 API URLs for ${hostName}, trying V1...`, + (e as Error).message, + ); + try { + return await getApiUrlsV1(url); + } catch (e) { + console.error(`[ERROR] Failed to get V1 API URLs for ${hostName}:`, (e as Error).message); + throw e; + } } } + +//region Well-Known V1 Interfaces + +interface WellKnownV1 { + api: string; +} + +export async function getApiUrlsV1(url: string): Promise { + const info: WellKnownV1 = await fetch(`${url}.well-known/spacebar`).then((res) => res.json()); + const api = info.api; + const apiUrl = new URL(api); + const policies: any = await fetch( + `${api}${apiUrl.pathname.includes("api") ? "" : "api"}/policies/instance/domains`, + ).then((res) => res.json()); + return { + api: policies.apiEndpoint, + gateway: policies.gateway, + cdn: policies.cdn, + wellknown: url, + }; +} +//endregion + +//region Well-Known V2 Interfaces +interface WellKnownV2BasicEndpoint { + baseUrl: string; +} + +interface WellKnownV2ApiVersions { + default: string; + active: string[]; +} + +interface WellKnownV2GatewayOptions { + encoding: ("json" | "etf")[]; + compression: ("zlib-stream" | "zstd-stream" | null)[]; +} + +interface WellKnownV2 { + admin?: WellKnownV2BasicEndpoint; + api: WellKnownV2BasicEndpoint & {apiVersions: WellKnownV2ApiVersions}; + cdn: WellKnownV2BasicEndpoint; + gateway: WellKnownV2BasicEndpoint & WellKnownV2GatewayOptions; +} + +export async function getApiUrlsV2(url: string): Promise { + const info: WellKnownV2 = await fetch(`${url}.well-known/spacebar/client`).then((res) => + res.json(), + ); + return { + api: info.api.baseUrl + "/api/v" + info.api.apiVersions.default, + gateway: info.gateway.baseUrl, + cdn: info.cdn.baseUrl, + wellknown: url, + }; +} +//endregion \ No newline at end of file From ae81c4ed55d52f6ec63dd6ce487d65652fcfa46b Mon Sep 17 00:00:00 2001 From: "[it/its]@Rory&" <[it/its]@Rory&> Date: Tue, 30 Dec 2025 14:36:27 +0100 Subject: [PATCH 03/10] typo fix: instace -> instance --- src/index.ts | 4 ++-- src/utils.ts | 4 ++-- src/webpage/settings.ts | 8 ++++---- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/index.ts b/src/index.ts index 042c0fef..51f32903 100644 --- a/src/index.ts +++ b/src/index.ts @@ -130,7 +130,7 @@ const app = http.createServer(async (req, res) => { sendFile(filePath); }); -export type instace = { +export type instance = { name: string; description?: string; descriptionLong?: string; @@ -157,7 +157,7 @@ export type instace = { }; const instances = JSON.parse( readFileSync(process.env.JANK_INSTANCES_PATH || __dirname + "/webpage/instances.json").toString(), -) as instace[]; +) as instance[]; const instanceNames = new Map(); diff --git a/src/utils.ts b/src/utils.ts index 4ef25665..1c7f543f 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,4 +1,4 @@ -import {instace} from "./index.js"; +import {instance} from "./index.js"; interface ApiUrls { api: string; @@ -9,7 +9,7 @@ interface ApiUrls { export async function getApiUrls( url: string, - instances: instace[], + instances: instance[], check = true, ): Promise { if (!url.endsWith("/")) { diff --git a/src/webpage/settings.ts b/src/webpage/settings.ts index 74fc7b41..ff8d00a5 100644 --- a/src/webpage/settings.ts +++ b/src/webpage/settings.ts @@ -1142,10 +1142,10 @@ class Options implements OptionsElement { onchange?: InstancePicker["onchange"], {button, instance}: {button?: HTMLButtonElement; instance?: string} = {}, ) { - const instacePicker = new InstancePicker(this, onchange, button, instance); - this.options.push(instacePicker); - this.generate(instacePicker); - return instacePicker; + const instancePicker = new InstancePicker(this, onchange, button, instance); + this.options.push(instancePicker); + this.generate(instancePicker); + return instancePicker; } returnFromSub() { this.subOptions = undefined; From 28fd2c3573db5a645917821ea91d556f6eec5c5e Mon Sep 17 00:00:00 2001 From: "[it/its]@Rory&" <[it/its]@Rory&> Date: Tue, 30 Dec 2025 14:39:07 +0100 Subject: [PATCH 04/10] typo fix: responce -> response --- src/webpage/service.ts | 16 ++++++++-------- src/webpage/utils/utils.ts | 8 ++++---- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/webpage/service.ts b/src/webpage/service.ts index 804d6958..d80eaedc 100644 --- a/src/webpage/service.ts +++ b/src/webpage/service.ts @@ -143,19 +143,19 @@ let fails = 0; async function getfile(req: Request): Promise { checkCache(); if (!samedomain(req.url) || enabled === "false" || (enabled === "offlineOnly" && !offline)) { - const responce = await fetch(req.clone()); + const response = await fetch(req.clone()); if (samedomain(req.url)) { - if (enabled === "offlineOnly" && responce.ok) { - putInCache(toPath(req.url), responce.clone()); + if (enabled === "offlineOnly" && response.ok) { + putInCache(toPath(req.url), response.clone()); } - if (!responce.ok) { + if (!response.ok) { fails++; if (fails > 5) { offline = true; } } } - return responce; + return response; } let path = toPath(req.url); @@ -228,12 +228,12 @@ self.addEventListener("fetch", async (e) => { if (apiHosts?.has(host || "")) { try { - const responce = await fetch(req.clone()); + const response = await fetch(req.clone()); try { - event.respondWith(responce.clone()); + event.respondWith(response.clone()); } catch {} - const json = await responce.json(); + const json = await response.json(); if (json._trace) { sendAll({ code: "trace", diff --git a/src/webpage/utils/utils.ts b/src/webpage/utils/utils.ts index 5f285406..aae89570 100644 --- a/src/webpage/utils/utils.ts +++ b/src/webpage/utils/utils.ts @@ -448,8 +448,8 @@ export async function getapiurls(str: string): Promise< } else { const val = stringURLsMap.get(str); if (val) { - const responce = await fetch(val.api + (val.api.endsWith("/") ? "" : "/") + "ping"); - if (responce.ok) { + const response = await fetch(val.api + (val.api.endsWith("/") ? "" : "/") + "ping"); + if (response.ok) { if (val.login) { return val as { wellknown: string; @@ -544,8 +544,8 @@ export async function getapiurls(str: string): Promise< } catch { const val = stringURLsMap.get(str); if (val) { - const responce = await fetch(val.api + (val.api.endsWith("/") ? "" : "/") + "ping"); - if (responce.ok) { + const response = await fetch(val.api + (val.api.endsWith("/") ? "" : "/") + "ping"); + if (response.ok) { if (val.login) { urls = val as { wellknown: string; From d0431000f32aa8f9cee97cd3f85b1f61285999ec Mon Sep 17 00:00:00 2001 From: "[it/its]@Rory&" <[it/its]@Rory&> Date: Tue, 30 Dec 2025 20:50:18 +0100 Subject: [PATCH 05/10] Mathium, fix your keyboard lol --- src/webpage/instances.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/webpage/instances.json b/src/webpage/instances.json index 7b415f9a..a5d49c56 100644 --- a/src/webpage/instances.json +++ b/src/webpage/instances.json @@ -20,7 +20,7 @@ "gateway": "wss://gateway-spacebar.greysilly7.xyz" }, "contactInfo": { - "dicord": "greysilly7", + "discord": "greysilly7", "github": "https://github.com/greysilly7", "email": "greysilly7@gmail.com" } From cc409b3c3d9c056d3aa7d6a39479398dc83d811b Mon Sep 17 00:00:00 2001 From: "[it/its]@Rory&" <[it/its]@Rory&> Date: Tue, 30 Dec 2025 21:40:05 +0100 Subject: [PATCH 06/10] It works! Let's gooooo!~ --- src/webpage/login.ts | 4 +- src/webpage/register.ts | 4 +- src/webpage/settings.ts | 10 +- src/webpage/utils/netUtils.ts | 8 ++ src/webpage/utils/utils.ts | 263 +++++++++++++++++----------------- 5 files changed, 150 insertions(+), 139 deletions(-) create mode 100644 src/webpage/utils/netUtils.ts diff --git a/src/webpage/login.ts b/src/webpage/login.ts index 4e961058..9463e099 100644 --- a/src/webpage/login.ts +++ b/src/webpage/login.ts @@ -2,6 +2,7 @@ import {instanceinfo, adduser, Specialuser} from "./utils/utils.js"; import {I18n} from "./i18n.js"; import {Dialog, FormError} from "./settings.js"; import {makeRegister} from "./register.js"; +import {trimTrailingSlashes} from "./utils/netUtils"; function generateRecArea(recover = document.getElementById("recover")) { if (!recover) return; recover.innerHTML = ""; @@ -55,8 +56,7 @@ export async function makeLogin( opt.addTitle(I18n.login.login()); const picker = opt.addInstancePicker( (info) => { - const api = info.login + (info.login.startsWith("/") ? "/" : ""); - form.fetchURL = api + "/auth/login"; + form.fetchURL = trimTrailingSlashes(info.api) + "/auth/login"; recover(info, rec); }, { diff --git a/src/webpage/register.ts b/src/webpage/register.ts index 52b23a4a..a658fc3f 100644 --- a/src/webpage/register.ts +++ b/src/webpage/register.ts @@ -3,6 +3,7 @@ import {adduser, Specialuser} from "./utils/utils.js"; import {makeLogin} from "./login.js"; import {MarkDown} from "./markdown.js"; import {Dialog, FormError} from "./settings.js"; +import {trimTrailingSlashes} from "./utils/netUtils"; export async function makeRegister( trasparentBg = false, instance = "", @@ -13,8 +14,7 @@ export async function makeRegister( opt.addTitle(I18n.htmlPages.createAccount()); const picker = opt.addInstancePicker( (info) => { - const api = info.login + (info.login.endsWith("/") ? "" : "/"); - form.fetchURL = api + "auth/register"; + form.fetchURL = trimTrailingSlashes(info.api) + "/auth/register"; tosLogic(md); }, {instance}, diff --git a/src/webpage/settings.ts b/src/webpage/settings.ts index ff8d00a5..edea1f6a 100644 --- a/src/webpage/settings.ts +++ b/src/webpage/settings.ts @@ -3,7 +3,7 @@ import { getInstances, getStringURLMapPair, instancefetch, - instanceinfo, + InstanceInfo, removeAni, } from "./utils/utils.js"; import {Emoji} from "./emoji.js"; @@ -890,13 +890,13 @@ class Dialog { removeAni(background); } } -class InstancePicker implements OptionsElement { - value: instanceinfo | null = null; +class InstancePicker implements OptionsElement { + value: InstanceInfo | null = null; owner: Options | Form; verify = document.createElement("p"); - onchange = (_: instanceinfo) => {}; + onchange = (_: InstanceInfo) => {}; instance?: string; - watchForChange(func: (arg1: instanceinfo) => void) { + watchForChange(func: (arg1: InstanceInfo) => void) { this.onchange = func; } constructor( diff --git a/src/webpage/utils/netUtils.ts b/src/webpage/utils/netUtils.ts new file mode 100644 index 00000000..c6b05da8 --- /dev/null +++ b/src/webpage/utils/netUtils.ts @@ -0,0 +1,8 @@ +export function trimTrailingSlashes(uri: string) { + if (!uri) return uri; + return uri.replace(/\/+$/, ""); +} + +export function isLoopback(str: string) { + return str.includes("localhost") || str.includes("127.0.0.1"); +} diff --git a/src/webpage/utils/utils.ts b/src/webpage/utils/utils.ts index aae89570..83013d66 100644 --- a/src/webpage/utils/utils.ts +++ b/src/webpage/utils/utils.ts @@ -3,6 +3,8 @@ import {MarkDown} from "../markdown.js"; import {Dialog} from "../settings.js"; import {fix} from "./cssMagic.js"; import {messageFrom, messageTo} from "./serviceType.js"; +import {isLoopback, trimTrailingSlashes} from "./netUtils"; + fix(); const apiDoms = new Set(); let instances: @@ -95,13 +97,7 @@ export function setDefaults() { } setDefaults(); export class Specialuser { - serverurls: { - api: string; - cdn: string; - gateway: string; - wellknown: string; - login: string; - }; + serverurls: InstanceUrls; email: string; token: string; loggedin; @@ -117,7 +113,6 @@ export class Specialuser { this.serverurls.cdn = new URL(json.serverurls.cdn).toString().replace(/\/$/, ""); this.serverurls.gateway = new URL(json.serverurls.gateway).toString().replace(/\/$/, ""); this.serverurls.wellknown = new URL(json.serverurls.wellknown).toString().replace(/\/$/, ""); - this.serverurls.login = new URL(json.serverurls.login).toString().replace(/\/$/, ""); this.email = json.email; this.token = json.token; this.loggedin = json.loggedin; @@ -416,33 +411,119 @@ const stringURLsMap = new Map< login?: string; } >(); + +export interface InstanceUrls { + admin?: string; + api: string; + cdn: string; + gateway: string; + wellknown: string; +} + +export interface InstanceInfo extends InstanceUrls { + value: string; +} + +export async function getapiurls(str: string): Promise { + str = str.trim(); + if (!str) return null; + + console.info("Attempting to fetch .well-known's for", str); + + // Override first: + let urls: InstanceUrls | null = await getInstanceInfo(str); + if (urls) return urls; + + // Otherwise, fall back to looking it up... + try { + urls = await getApiUrlsV2(str); + if (!urls) throw new Error("meow"); + return urls; + } catch { + return await getApiUrlsV1(str); + } +} + +//region Instance list +export async function getInstanceInfo(str: string): Promise { + // wait for it to be loaded...? Where is this even comming from? + if (stringURLMap.size == 0) { + await new Promise((res, _) => { + let intervalId = setInterval(() => { + if (stringURLMap.size !== 0) { + clearInterval(intervalId); + res(); + } + }, 10); + }); + } + + console.info("Checking if we already know", str, "in our instance lists:", { + stringURLMap, + stringURLsMap, + }); + + if (stringURLMap.has(str)) { + console.error("OOH WE GOT STRING->URL MAP ENTRY FOR", str, "!!!!", stringURLMap.get(str)); + return (await getapiurls(stringURLMap.get(str)!)) as InstanceInfo; + } + + if (stringURLsMap.has(str)) { + console.error( + "WE GOT URL->INSTANCE MAP ENTRY FOR ", + str, + "!!!!!!!!!!11", + stringURLsMap.get(str), + ); + return stringURLsMap.get(str) as InstanceInfo; + } + + return null; +} +//endregion + +//region Well-Known v2 +export async function getApiUrlsV2(str: string): Promise { + if (!URL.canParse(str)) { + console.log("getApiUrlsV2:", str, "is not a parseable url"); + return null; + } + try { + const info = await fetch(str + "/.well-known/spacebar/client").then((r) => r.json()); + return { + admin: info.admin?.baseUrl, + api: info.api.baseUrl + "/api/v" + info.api.apiVersions.default, + gateway: info.gateway.baseUrl, + cdn: info.cdn.baseUrl, + wellknown: str, + }; + } catch (e) { + console.log("No .well-known v2 for", str, (e as Error).message); + return null; + } +} +//endregion + +//region Well-Known v1 /** - * this fucntion checks if a string is an instance, it'll either return the API urls or false + * this function checks if a string is an instance, it'll either return the API urls or null */ -export async function getapiurls(str: string): Promise< - | { - api: string; - cdn: string; - gateway: string; - wellknown: string; - login: string; - } - | false -> { +export async function getApiUrlsV1(str: string): Promise { function appendApi(str: string) { return str.includes("api") ? str : str.endsWith("/") ? str + "api" : str + "/api"; } if (!URL.canParse(str)) { - const val = stringURLMap.get(str); if (stringURLMap.size === 0) { await new Promise((res) => { - setInterval(() => { + let intervalID = setInterval(() => { if (stringURLMap.size !== 0) { + clearInterval(intervalID); res(); } }, 100); }); } + const val = stringURLMap.get(str); if (val) { str = val; } else { @@ -451,22 +532,10 @@ export async function getapiurls(str: string): Promise< const response = await fetch(val.api + (val.api.endsWith("/") ? "" : "/") + "ping"); if (response.ok) { if (val.login) { - return val as { - wellknown: string; - api: string; - cdn: string; - gateway: string; - login: string; - }; + return val as InstanceUrls; } else { val.login = val.api; - return val as { - wellknown: string; - api: string; - cdn: string; - gateway: string; - login: string; - }; + return val as InstanceUrls; } } } else if (!str.match(/^https?:\/\//gm)) { @@ -480,54 +549,25 @@ export async function getapiurls(str: string): Promise< let api: string; try { const info = await fetch(`${str}.well-known/spacebar`).then((x) => x.json()); - if (info.api.endsWith("/")) { - const temp = info.api.split("/"); - temp.pop(); - info.api = temp.join("/"); - } - api = info.api; + api = trimTrailingSlashes(info.api); } catch { api = str; } if (!URL.canParse(api)) { - return false; + return null; } const url = new URL(api); - function isloopback(str: string) { - return str.includes("localhost") || str.includes("127.0.0.1"); - } - let urls: - | undefined - | { - api: string; - cdn: string; - gateway: string; - wellknown: string; - login: string; - }; + let urls: undefined | InstanceUrls; function fixApi() { if (!urls) return; - if (urls.api.endsWith("/")) { - const split = urls.api.split("/"); - split.pop(); - urls.api = split.join("/"); - } - if (urls.cdn.endsWith("/")) { - const split = urls.cdn.split("/"); - split.pop(); - urls.cdn = split.join("/"); - } - if (urls.gateway.endsWith("/")) { - const split = urls.gateway.split("/"); - split.pop(); - urls.gateway = split.join("/"); - } - if (urls.login.endsWith("/")) { - const split = urls.login.split("/"); - split.pop(); - urls.login = split.join("/"); - } + urls = { + wellknown: trimTrailingSlashes(urls.wellknown), + api: trimTrailingSlashes(urls.api), + cdn: trimTrailingSlashes(urls.cdn), + gateway: trimTrailingSlashes(urls.gateway), + }; } + try { const info = await fetch( `${api}${url.pathname.includes("api") ? "" : "api"}/policies/instance/domains`, @@ -538,49 +578,27 @@ export async function getapiurls(str: string): Promise< gateway: info.gateway, cdn: info.cdn, wellknown: str, - login: apiurl.origin + appendApi(apiurl.pathname), }; fixApi(); } catch { const val = stringURLsMap.get(str); if (val) { - const response = await fetch(val.api + (val.api.endsWith("/") ? "" : "/") + "ping"); + const response = await fetch(trimTrailingSlashes(val.api) + "/ping"); if (response.ok) { if (val.login) { - urls = val as { - wellknown: string; - api: string; - cdn: string; - gateway: string; - login: string; - }; + urls = val as InstanceUrls; fixApi(); } else { val.login = val.api; - urls = val as { - wellknown: string; - api: string; - cdn: string; - gateway: string; - login: string; - }; + urls = val as InstanceUrls; fixApi(); } } } } if (urls) { - if (isloopback(urls.api) !== isloopback(str)) { - return new Promise< - | { - api: string; - cdn: string; - gateway: string; - wellknown: string; - login: string; - } - | false - >((res) => { + if (isLoopback(urls.api) !== isLoopback(str)) { + return new Promise((res) => { const menu = new Dialog(""); const options = menu.float.options; options.addMDText(new MarkDown(I18n.incorrectURLS(), undefined)); @@ -589,6 +607,7 @@ export async function getapiurls(str: string): Promise< opt.addButtonInput("", I18n.yes(), async () => { if (clicked) return; clicked = true; + if (urls == null) throw new Error("How the fuck was `urls` null here?"); const temp = new URL(str); temp.port = ""; const newOrgin = temp.host; @@ -598,7 +617,6 @@ export async function getapiurls(str: string): Promise< cdn: new URL(urls.cdn), gateway: new URL(urls.gateway), wellknown: new URL(urls.wellknown), - login: new URL(urls.login), }; tempurls.api.host = newOrgin; tempurls.api.protocol = protical; @@ -612,9 +630,6 @@ export async function getapiurls(str: string): Promise< tempurls.wellknown.host = newOrgin; tempurls.wellknown.protocol = protical; - tempurls.login.host = newOrgin; - tempurls.login.protocol = protical; - try { if ( !( @@ -623,12 +638,12 @@ export async function getapiurls(str: string): Promise< ) ).ok ) { - res(false); + res(null); menu.hide(); return; } } catch { - res(false); + res(null); menu.hide(); return; } @@ -637,23 +652,23 @@ export async function getapiurls(str: string): Promise< cdn: tempurls.cdn.toString(), gateway: tempurls.gateway.toString(), wellknown: tempurls.wellknown.toString(), - login: tempurls.login.toString(), }); menu.hide(); }); const no = opt.addButtonInput("", I18n.no(), async () => { if (clicked) return; clicked = true; + if (urls == null) throw new Error("How the fuck was `urls` null here?"); try { //TODO make this a promise race for when the server just never responds //TODO maybe try to strip ports as another way to fix it if (!(await fetch(urls.api + "ping")).ok) { - res(false); + res(null); menu.hide(); return; } } catch { - res(false); + res(null); return; } res(urls); @@ -673,15 +688,17 @@ export async function getapiurls(str: string): Promise< //*/ try { if (!(await fetch(urls.api + "/ping")).ok) { - return false; + return null; } } catch { - return false; + return null; } return urls; } - return false; + return null; } +//endregion + async function isAnimated(src: string) { try { src = new URL(src).pathname; @@ -780,14 +797,7 @@ export function createImg( }, }); } -export interface instanceinfo { - wellknown: string; - api: string; - cdn: string; - gateway: string; - login: string; - value: string; -} + /** * * This function takes in a string and checks if the string is a valid instance @@ -807,7 +817,7 @@ const checkInstance = Object.assign( loginButton.disabled = true; verify!.textContent = I18n.login.checking(); const instanceValue = instance; - const instanceinfo = (await getapiurls(instanceValue)) as instanceinfo; + const instanceinfo = (await getapiurls(instanceValue)) as InstanceInfo; if (instanceinfo) { instanceinfo.value = instanceValue; localStorage.setItem("instanceinfo", JSON.stringify(instanceinfo)); @@ -834,14 +844,7 @@ const checkInstance = Object.assign( } }, {} as { - alt?: (e: { - wellknown: string; - api: string; - cdn: string; - gateway: string; - login: string; - value: string; - }) => void; + alt?: (e: InstanceInfo) => void; }, ); { From acddb488a599cf3cd7bb7a4cbd3c0e8f667a37a2 Mon Sep 17 00:00:00 2001 From: "[it/its]@Rory&" <[it/its]@Rory&> Date: Tue, 30 Dec 2025 21:42:04 +0100 Subject: [PATCH 07/10] Typo fix: protical -> protocol --- src/webpage/markdown.ts | 4 ++-- src/webpage/utils/utils.ts | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/webpage/markdown.ts b/src/webpage/markdown.ts index a584131a..e936f004 100644 --- a/src/webpage/markdown.ts +++ b/src/webpage/markdown.ts @@ -719,8 +719,8 @@ class MarkDown { } if (URL.canParse(build) && txt[j] === ">") { const url = new URL(build); - const allowedProticals = new Set(["https:", "http:"]); - if (allowedProticals.has(url.protocol)) { + const allowedprotocols = new Set(["https:", "http:"]); + if (allowedprotocols.has(url.protocol)) { i = j; if (keep) { diff --git a/src/webpage/utils/utils.ts b/src/webpage/utils/utils.ts index 83013d66..7e513cd2 100644 --- a/src/webpage/utils/utils.ts +++ b/src/webpage/utils/utils.ts @@ -611,7 +611,7 @@ export async function getApiUrlsV1(str: string): Promise { const temp = new URL(str); temp.port = ""; const newOrgin = temp.host; - const protical = temp.protocol; + const protocol = temp.protocol; const tempurls = { api: new URL(urls.api), cdn: new URL(urls.cdn), @@ -619,16 +619,16 @@ export async function getApiUrlsV1(str: string): Promise { wellknown: new URL(urls.wellknown), }; tempurls.api.host = newOrgin; - tempurls.api.protocol = protical; + tempurls.api.protocol = protocol; tempurls.cdn.host = newOrgin; - tempurls.api.protocol = protical; + tempurls.api.protocol = protocol; tempurls.gateway.host = newOrgin; tempurls.gateway.protocol = temp.protocol === "http:" ? "ws:" : "wss:"; tempurls.wellknown.host = newOrgin; - tempurls.wellknown.protocol = protical; + tempurls.wellknown.protocol = protocol; try { if ( From 1fef678ffbc5540a5fd2182a236081135cca9b28 Mon Sep 17 00:00:00 2001 From: "[it/its]@Rory&" <[it/its]@Rory&> Date: Tue, 30 Dec 2025 21:42:46 +0100 Subject: [PATCH 08/10] Typo fix: orgin -> origin --- src/webpage/localuser.ts | 8 ++++---- src/webpage/utils/utils.ts | 10 +++++----- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/webpage/localuser.ts b/src/webpage/localuser.ts index 04267e13..828d6987 100644 --- a/src/webpage/localuser.ts +++ b/src/webpage/localuser.ts @@ -4026,7 +4026,7 @@ class Localuser { remove(); } } - MDFindChannel(name: string, orginal: string, box: HTMLDivElement, typebox: MarkDown) { + MDFindChannel(name: string, original: string, box: HTMLDivElement, typebox: MarkDown) { const maybe: [number, Channel][] = []; if (this.lookingguild && this.lookingguild.id !== "@me") { for (const channel of this.lookingguild.channels) { @@ -4039,7 +4039,7 @@ class Localuser { maybe.sort((a, b) => b[0] - a[0]); this.MDSearchOptions( maybe.map((a) => ["# " + a[1].name, `<#${a[1].id}> `, undefined]), - orginal, + original, box, typebox, ); @@ -4113,7 +4113,7 @@ class Localuser { } } } - findEmoji(search: string, orginal: string, box: HTMLDivElement, typebox: MarkDown) { + findEmoji(search: string, original: string, box: HTMLDivElement, typebox: MarkDown) { const emj = Emoji.searchEmoji(search, this, 10); const map = emj.map(([emoji]): [string, string, HTMLElement, () => void] => { return [ @@ -4127,7 +4127,7 @@ class Localuser { }, ]; }); - this.MDSearchOptions(map, orginal, box, typebox); + this.MDSearchOptions(map, original, box, typebox); } async findCommands(search: string, box: HTMLDivElement, md: MarkDown) { const guild = this.lookingguild; diff --git a/src/webpage/utils/utils.ts b/src/webpage/utils/utils.ts index 7e513cd2..10f201a4 100644 --- a/src/webpage/utils/utils.ts +++ b/src/webpage/utils/utils.ts @@ -610,7 +610,7 @@ export async function getApiUrlsV1(str: string): Promise { if (urls == null) throw new Error("How the fuck was `urls` null here?"); const temp = new URL(str); temp.port = ""; - const newOrgin = temp.host; + const newOrigin = temp.host; const protocol = temp.protocol; const tempurls = { api: new URL(urls.api), @@ -618,16 +618,16 @@ export async function getApiUrlsV1(str: string): Promise { gateway: new URL(urls.gateway), wellknown: new URL(urls.wellknown), }; - tempurls.api.host = newOrgin; + tempurls.api.host = newOrigin; tempurls.api.protocol = protocol; - tempurls.cdn.host = newOrgin; + tempurls.cdn.host = newOrigin; tempurls.api.protocol = protocol; - tempurls.gateway.host = newOrgin; + tempurls.gateway.host = newOrigin; tempurls.gateway.protocol = temp.protocol === "http:" ? "ws:" : "wss:"; - tempurls.wellknown.host = newOrgin; + tempurls.wellknown.host = newOrigin; tempurls.wellknown.protocol = protocol; try { From 5d61da1b51c2062408e7372a1a31b9cbb11a2451 Mon Sep 17 00:00:00 2001 From: MathMan05 Date: Wed, 31 Dec 2025 20:01:34 -0600 Subject: [PATCH 09/10] little modification --- src/webpage/utils/utils.ts | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/webpage/utils/utils.ts b/src/webpage/utils/utils.ts index 10f201a4..595fbe1e 100644 --- a/src/webpage/utils/utils.ts +++ b/src/webpage/utils/utils.ts @@ -543,12 +543,10 @@ export async function getApiUrlsV1(str: string): Promise { } } } - if (str.at(-1) !== "/") { - str += "/"; - } + str = trimTrailingSlashes(str); let api: string; try { - const info = await fetch(`${str}.well-known/spacebar`).then((x) => x.json()); + const info = await fetch(`${str}/.well-known/spacebar`).then((x) => x.json()); api = trimTrailingSlashes(info.api); } catch { api = str; @@ -607,7 +605,7 @@ export async function getApiUrlsV1(str: string): Promise { opt.addButtonInput("", I18n.yes(), async () => { if (clicked) return; clicked = true; - if (urls == null) throw new Error("How the fuck was `urls` null here?"); + if (urls == null) throw new Error("Unexpected undefined, exiting"); const temp = new URL(str); temp.port = ""; const newOrigin = temp.host; @@ -658,7 +656,7 @@ export async function getApiUrlsV1(str: string): Promise { const no = opt.addButtonInput("", I18n.no(), async () => { if (clicked) return; clicked = true; - if (urls == null) throw new Error("How the fuck was `urls` null here?"); + if (urls == null) throw new Error("URLs is undefined"); try { //TODO make this a promise race for when the server just never responds //TODO maybe try to strip ports as another way to fix it @@ -837,7 +835,6 @@ const checkInstance = Object.assign( return; } } catch { - console.log("catch"); verify!.textContent = I18n.login.invalid(); loginButton.disabled = true; return; From 7dbd3b6c650e87aac3dcc4c38f410439e2e6f5e2 Mon Sep 17 00:00:00 2001 From: "[it/its]@Rory&" <[it/its]@Rory&> Date: Tue, 30 Dec 2025 21:51:55 +0100 Subject: [PATCH 10/10] Well known v2 support This patchset contains support for .well-known/spacebar/client, as well as various typo fixes and some minor cleanups. Kind regards, Emma [it/its] @ Rory& Emma [it/its]@Rory& (7): src/utils: Well-known v2 support typo fix: instace -> instance typo fix: responce -> response Mathium, fix your keyboard lol It works! Let's gooooo!~ Typo fix: protical -> protocol Typo fix: orgin -> origin src/index.ts | 4 +- src/utils.ts | 96 +++++++++--- src/webpage/instances.json | 2 +- src/webpage/localuser.ts | 8 +- src/webpage/login.ts | 4 +- src/webpage/markdown.ts | 4 +- src/webpage/register.ts | 4 +- src/webpage/service.ts | 16 +- src/webpage/settings.ts | 18 +-- src/webpage/utils/netUtils.ts | 8 + src/webpage/utils/utils.ts | 287 +++++++++++++++++----------------- 11 files changed, 260 insertions(+), 191 deletions(-) create mode 100644 src/webpage/utils/netUtils.ts -- 2.51.2 -- 2.51.2