Skip to content
46 linesCodeBlameRaw
1import { DISCOVERY_PATH, parseDiscovery } from "@g1t/contracts/discovery";
2
3import { type Server, parseOrigin, parseServer } from "./server";
4
5/**
6 * A g1t from the address someone typed, or a `g1t://connect` link gave:
7 * its site, and its API as the site says (`/.well-known/g1t.json`,
8 * packages/contracts src/discovery.ts), so a self-hosted g1t's API can be
9 * anywhere. A g1t from before the phone app has no such file: its API is
10 * then taken to be `api.` in front of the site, and Chat may need a
11 * website-enabled access token there.
12 */
13export type Found = { server: Server; known: boolean; push: boolean; chat: boolean };
14
15export async function discover(siteText: string, apiText?: string): Promise<Found> {
16 const site = parseOrigin(siteText);
17 if (!site) throw new Error("That is not a web address. It looks like https://g1t.example.com.");
18 if (apiText?.trim()) {
19 const given = parseServer(site, apiText);
20 if (!given) throw new Error("The API's address is not a web address.");
21 return { server: given, known: false, push: true, chat: true };
22 }
23 const controller = new AbortController();
24 const timer = setTimeout(() => controller.abort(), 6000);
25 try {
26 const response = await fetch(site + DISCOVERY_PATH, { headers: { accept: "application/json" }, signal: controller.signal });
27 const found = response.ok ? parseDiscovery(await response.json()) : null;
28 if (found) {
29 return { server: { site: found.site, api: found.api, push: found.phone.push }, known: true, push: found.phone.push, chat: found.phone.chat };
30 }
31 } catch {
32 // Unreachable, or not g1t: said below.
33 } finally {
34 clearTimeout(timer);
35 }
36 const guessed = parseServer(site);
37 if (!guessed) throw new Error("That is not a web address.");
38 // Reachable at all? A wrong address should say so now, not at sign-in.
39 try {
40 const metadata = await fetch(`${guessed.api}/.well-known/oauth-authorization-server`, { headers: { accept: "application/json" } });
41 if (!metadata.ok) throw new Error(String(metadata.status));
42 } catch {
43 throw new Error(`${new URL(site).host} did not answer as a g1t. Check the address, or give its API's address too.`);
44 }
45 return { server: guessed, known: false, push: true, chat: false };
46}