@linkshell/gateway 0.5.3 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Dockerfile +8 -22
- package/README.md +99 -44
- package/dist/gateway/src/accounts.js.map +1 -0
- package/dist/gateway/src/index.d.ts +4 -1
- package/dist/gateway/src/index.js +4 -863
- package/dist/gateway/src/index.js.map +1 -1
- package/dist/gateway/src/main.js +57 -0
- package/dist/gateway/src/main.js.map +1 -0
- package/dist/gateway/src/rate-limit.d.ts +22 -0
- package/dist/gateway/src/rate-limit.js +66 -0
- package/dist/gateway/src/rate-limit.js.map +1 -0
- package/dist/gateway/src/relay.d.ts +84 -5
- package/dist/gateway/src/relay.js +402 -529
- package/dist/gateway/src/relay.js.map +1 -1
- package/dist/gateway/src/serve.d.ts +34 -0
- package/dist/gateway/src/serve.js +102 -0
- package/dist/gateway/src/serve.js.map +1 -0
- package/dist/{gateway-v2 → gateway}/src/store.d.ts +5 -0
- package/dist/{gateway-v2 → gateway}/src/store.js +7 -0
- package/dist/gateway/src/store.js.map +1 -0
- package/dist/gateway/src/subscription.d.ts +18 -0
- package/dist/gateway/src/subscription.js +46 -0
- package/dist/gateway/src/subscription.js.map +1 -0
- package/dist/gateway/tsconfig.tsbuildinfo +1 -1
- package/dist/wire/src/direct.d.ts +22 -0
- package/dist/wire/src/direct.js +37 -0
- package/dist/wire/src/direct.js.map +1 -0
- package/dist/wire/src/index.d.ts +1 -0
- package/dist/wire/src/index.js +1 -0
- package/dist/wire/src/index.js.map +1 -1
- package/dist/wire/src/model.d.ts +287 -6
- package/dist/wire/src/model.js +33 -0
- package/dist/wire/src/model.js.map +1 -1
- package/dist/wire/src/relay.d.ts +8 -8
- package/dist/wire/src/rpc.d.ts +1780 -44
- package/dist/wire/src/rpc.js +48 -3
- package/dist/wire/src/rpc.js.map +1 -1
- package/dist/wire/src/updates.d.ts +356 -0
- package/dist/wire/src/updates.js +11 -1
- package/dist/wire/src/updates.js.map +1 -1
- package/package.json +12 -12
- package/src/accounts.ts +32 -0
- package/src/index.ts +4 -997
- package/src/main.ts +65 -0
- package/src/rate-limit.ts +72 -0
- package/src/relay.ts +435 -620
- package/src/serve.ts +130 -0
- package/src/store.ts +135 -0
- package/src/subscription.ts +51 -0
- package/dist/gateway/src/auth-middleware.d.ts +0 -44
- package/dist/gateway/src/auth-middleware.js +0 -204
- package/dist/gateway/src/auth-middleware.js.map +0 -1
- package/dist/gateway/src/cors.d.ts +0 -2
- package/dist/gateway/src/cors.js +0 -7
- package/dist/gateway/src/cors.js.map +0 -1
- package/dist/gateway/src/embedded.d.ts +0 -21
- package/dist/gateway/src/embedded.js +0 -438
- package/dist/gateway/src/embedded.js.map +0 -1
- package/dist/gateway/src/host-auth.d.ts +0 -32
- package/dist/gateway/src/host-auth.js +0 -78
- package/dist/gateway/src/host-auth.js.map +0 -1
- package/dist/gateway/src/pairings.d.ts +0 -38
- package/dist/gateway/src/pairings.js +0 -119
- package/dist/gateway/src/pairings.js.map +0 -1
- package/dist/gateway/src/presence.d.ts +0 -28
- package/dist/gateway/src/presence.js +0 -92
- package/dist/gateway/src/presence.js.map +0 -1
- package/dist/gateway/src/sessions.d.ts +0 -113
- package/dist/gateway/src/sessions.js +0 -352
- package/dist/gateway/src/sessions.js.map +0 -1
- package/dist/gateway/src/state-store.d.ts +0 -21
- package/dist/gateway/src/state-store.js +0 -103
- package/dist/gateway/src/state-store.js.map +0 -1
- package/dist/gateway/src/static-web.d.ts +0 -15
- package/dist/gateway/src/static-web.js +0 -129
- package/dist/gateway/src/static-web.js.map +0 -1
- package/dist/gateway/src/tokens.d.ts +0 -19
- package/dist/gateway/src/tokens.js +0 -151
- package/dist/gateway/src/tokens.js.map +0 -1
- package/dist/gateway/src/tunnel.d.ts +0 -61
- package/dist/gateway/src/tunnel.js +0 -576
- package/dist/gateway/src/tunnel.js.map +0 -1
- package/dist/gateway-v2/src/accounts.js.map +0 -1
- package/dist/gateway-v2/src/index.d.ts +0 -3
- package/dist/gateway-v2/src/index.js +0 -4
- package/dist/gateway-v2/src/index.js.map +0 -1
- package/dist/gateway-v2/src/main.js +0 -17
- package/dist/gateway-v2/src/main.js.map +0 -1
- package/dist/gateway-v2/src/server.d.ts +0 -84
- package/dist/gateway-v2/src/server.js +0 -426
- package/dist/gateway-v2/src/server.js.map +0 -1
- package/dist/gateway-v2/src/store.js.map +0 -1
- package/dist/shared-protocol/src/index.d.ts +0 -17029
- package/dist/shared-protocol/src/index.js +0 -1064
- package/dist/shared-protocol/src/index.js.map +0 -1
- package/src/auth-middleware.ts +0 -266
- package/src/cors.ts +0 -8
- package/src/embedded.ts +0 -526
- package/src/host-auth.ts +0 -88
- package/src/pairings.ts +0 -133
- package/src/presence.ts +0 -118
- package/src/sessions.ts +0 -432
- package/src/state-store.ts +0 -140
- package/src/static-web.ts +0 -133
- package/src/tokens.ts +0 -159
- package/src/tunnel.ts +0 -644
- /package/dist/{gateway-v2 → gateway}/src/accounts.d.ts +0 -0
- /package/dist/{gateway-v2 → gateway}/src/accounts.js +0 -0
- /package/dist/{gateway-v2 → gateway}/src/main.d.ts +0 -0
package/src/serve.ts
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { mkdirSync } from "node:fs";
|
|
2
|
+
import { createServer, type IncomingMessage, type ServerResponse } from "node:http";
|
|
3
|
+
import { createRequire } from "node:module";
|
|
4
|
+
import { dirname } from "node:path";
|
|
5
|
+
import { RELAY_PATH } from "@linkshell/wire";
|
|
6
|
+
import type { VerifyToken } from "./accounts.js";
|
|
7
|
+
import { clientIp, isLoopback, RateLimiter, trustedProxySet } from "./rate-limit.js";
|
|
8
|
+
import { Gateway, type GatewayOptions } from "./relay.js";
|
|
9
|
+
|
|
10
|
+
export interface StartGatewayOptions {
|
|
11
|
+
port: number;
|
|
12
|
+
/** The interface to listen on; all of them when omitted. */
|
|
13
|
+
host?: string;
|
|
14
|
+
/** SQLite file holding pairings and peers' public keys. Losing it unpairs every phone. */
|
|
15
|
+
databasePath: string;
|
|
16
|
+
/** What `/healthz` reports; this package's version when omitted. */
|
|
17
|
+
version?: string;
|
|
18
|
+
log?: (message: string) => void;
|
|
19
|
+
/** Validates account tokens; without it, accounts are off and only pairing works. */
|
|
20
|
+
verifyToken?: VerifyToken;
|
|
21
|
+
admit?: GatewayOptions["admit"];
|
|
22
|
+
/** Addresses of the reverse proxies in front, whose X-Forwarded-For is believed. */
|
|
23
|
+
trustedProxies?: readonly string[];
|
|
24
|
+
/** Connections one address may open per window (default 20 a minute). */
|
|
25
|
+
wsConnectLimit?: { max: number; windowMs: number };
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface RunningGateway {
|
|
29
|
+
port: number;
|
|
30
|
+
relay: Gateway;
|
|
31
|
+
close(): Promise<void>;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* The gateway as it is deployed: one HTTP server with the relay on
|
|
36
|
+
* `RELAY_PATH` and a health check on `/healthz`. Nothing else is served.
|
|
37
|
+
*/
|
|
38
|
+
export async function startGateway(options: StartGatewayOptions): Promise<RunningGateway> {
|
|
39
|
+
mkdirSync(dirname(options.databasePath), { recursive: true });
|
|
40
|
+
const relay = new Gateway({
|
|
41
|
+
port: 0,
|
|
42
|
+
databasePath: options.databasePath,
|
|
43
|
+
verifyToken: options.verifyToken,
|
|
44
|
+
admit: options.admit,
|
|
45
|
+
log: options.log,
|
|
46
|
+
});
|
|
47
|
+
const version = options.version ?? packageVersion();
|
|
48
|
+
const trustedProxies = trustedProxySet(options.trustedProxies ?? []);
|
|
49
|
+
const limit = options.wsConnectLimit ?? { max: 20, windowMs: 60_000 };
|
|
50
|
+
const connectLimiter = new RateLimiter(limit.max, limit.windowMs);
|
|
51
|
+
|
|
52
|
+
const server = createServer((request, response) => {
|
|
53
|
+
if (request.method === "GET" && pathOf(request) === "/healthz") {
|
|
54
|
+
// memoryMb: what the process holds, to watch against the container's limit.
|
|
55
|
+
json(response, 200, { ok: true, version, relay: relay.connected, memoryMb: Math.round(process.memoryUsage.rss() / 1048576) });
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
json(response, 404, { error: "not_found" });
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
server.on("upgrade", (request, socket, head) => {
|
|
62
|
+
if (pathOf(request) !== RELAY_PATH) {
|
|
63
|
+
socket.destroy();
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
const ip = clientIp(request, trustedProxies);
|
|
67
|
+
if (!isLoopback(ip) && !connectLimiter.allow(ip)) {
|
|
68
|
+
socket.write("HTTP/1.1 429 Too Many Requests\r\n\r\n");
|
|
69
|
+
socket.destroy();
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
relay.handleUpgrade(request, socket, head);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
try {
|
|
76
|
+
await new Promise<void>((resolve, reject) => {
|
|
77
|
+
server.once("error", reject);
|
|
78
|
+
server.listen(options.port, options.host, () => {
|
|
79
|
+
server.off("error", reject);
|
|
80
|
+
resolve();
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
} catch (error) {
|
|
84
|
+
connectLimiter.destroy();
|
|
85
|
+
await relay.stop();
|
|
86
|
+
throw error;
|
|
87
|
+
}
|
|
88
|
+
relay.attach();
|
|
89
|
+
|
|
90
|
+
const address = server.address();
|
|
91
|
+
return {
|
|
92
|
+
port: typeof address === "object" && address ? address.port : options.port,
|
|
93
|
+
relay,
|
|
94
|
+
close: async () => {
|
|
95
|
+
connectLimiter.destroy();
|
|
96
|
+
// The relay drops its peers first: the server only finishes closing once their sockets are gone.
|
|
97
|
+
await relay.stop();
|
|
98
|
+
await new Promise<void>((resolve) => server.close(() => resolve()));
|
|
99
|
+
},
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/** The request's path; undefined for a target that doesn't parse, which then matches nothing. */
|
|
104
|
+
function pathOf(request: IncomingMessage): string | undefined {
|
|
105
|
+
try {
|
|
106
|
+
return new URL(request.url ?? "/", "http://gateway").pathname;
|
|
107
|
+
} catch {
|
|
108
|
+
return undefined;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function json(response: ServerResponse, status: number, body: unknown): void {
|
|
113
|
+
response.writeHead(status, { "content-type": "application/json" });
|
|
114
|
+
response.end(JSON.stringify(body));
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/** This package's version, so `/healthz` says which build is deployed. */
|
|
118
|
+
export function packageVersion(): string {
|
|
119
|
+
const require = createRequire(import.meta.url);
|
|
120
|
+
// package.json is one level up from src/ and three up from dist/gateway/src/.
|
|
121
|
+
for (const path of ["../package.json", "../../../package.json"]) {
|
|
122
|
+
try {
|
|
123
|
+
const manifest = require(path) as { name?: string; version?: string };
|
|
124
|
+
if (manifest.name === "@linkshell/gateway" && manifest.version) return manifest.version;
|
|
125
|
+
} catch {
|
|
126
|
+
// Not at this depth.
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
return "unknown";
|
|
130
|
+
}
|
package/src/store.ts
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { DatabaseSync } from "node:sqlite";
|
|
2
|
+
import type { PeerRole } from "@linkshell/wire";
|
|
3
|
+
|
|
4
|
+
// What the gateway remembers: peers' public keys and account, and which
|
|
5
|
+
// devices are paired with which machines. No content, ever.
|
|
6
|
+
|
|
7
|
+
export interface PeerRecord {
|
|
8
|
+
id: string;
|
|
9
|
+
role: PeerRole;
|
|
10
|
+
signKey: string;
|
|
11
|
+
boxKey: string;
|
|
12
|
+
name: string;
|
|
13
|
+
platform?: string;
|
|
14
|
+
userId?: string;
|
|
15
|
+
lastSeen: number;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export class GatewayStore {
|
|
19
|
+
private readonly db: DatabaseSync;
|
|
20
|
+
|
|
21
|
+
constructor(path: string) {
|
|
22
|
+
this.db = new DatabaseSync(path);
|
|
23
|
+
this.db.exec(`
|
|
24
|
+
PRAGMA journal_mode = WAL;
|
|
25
|
+
CREATE TABLE IF NOT EXISTS peers (
|
|
26
|
+
id TEXT PRIMARY KEY,
|
|
27
|
+
role TEXT NOT NULL,
|
|
28
|
+
sign_key TEXT NOT NULL,
|
|
29
|
+
box_key TEXT NOT NULL,
|
|
30
|
+
name TEXT NOT NULL,
|
|
31
|
+
platform TEXT,
|
|
32
|
+
user_id TEXT,
|
|
33
|
+
last_seen INTEGER NOT NULL
|
|
34
|
+
);
|
|
35
|
+
CREATE INDEX IF NOT EXISTS peers_by_user ON peers (user_id);
|
|
36
|
+
CREATE TABLE IF NOT EXISTS links (
|
|
37
|
+
machine_id TEXT NOT NULL,
|
|
38
|
+
device_id TEXT NOT NULL,
|
|
39
|
+
created_at INTEGER NOT NULL,
|
|
40
|
+
PRIMARY KEY (machine_id, device_id)
|
|
41
|
+
) WITHOUT ROWID;
|
|
42
|
+
CREATE INDEX IF NOT EXISTS links_by_device ON links (device_id);
|
|
43
|
+
`);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
close(): void {
|
|
47
|
+
this.db.close();
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
peer(id: string): PeerRecord | undefined {
|
|
51
|
+
const row = this.db.prepare("SELECT * FROM peers WHERE id = ?").get(id) as Record<string, unknown> | undefined;
|
|
52
|
+
return row ? toPeer(row) : undefined;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
upsertPeer(peer: PeerRecord): void {
|
|
56
|
+
this.db
|
|
57
|
+
.prepare(
|
|
58
|
+
`INSERT INTO peers (id, role, sign_key, box_key, name, platform, user_id, last_seen)
|
|
59
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
60
|
+
ON CONFLICT(id) DO UPDATE SET box_key = excluded.box_key, name = excluded.name,
|
|
61
|
+
platform = excluded.platform, user_id = excluded.user_id, last_seen = excluded.last_seen`,
|
|
62
|
+
)
|
|
63
|
+
.run(peer.id, peer.role, peer.signKey, peer.boxKey, peer.name, peer.platform ?? null, peer.userId ?? null, peer.lastSeen);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
touch(id: string, at = Date.now()): void {
|
|
67
|
+
this.db.prepare("UPDATE peers SET last_seen = ? WHERE id = ?").run(at, id);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
link(machineId: string, deviceId: string, at = Date.now()): void {
|
|
71
|
+
this.db.prepare("INSERT OR IGNORE INTO links (machine_id, device_id, created_at) VALUES (?, ?, ?)").run(machineId, deviceId, at);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
unlink(machineId: string, deviceId: string): boolean {
|
|
75
|
+
return this.db.prepare("DELETE FROM links WHERE machine_id = ? AND device_id = ?").run(machineId, deviceId).changes > 0;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Takes a peer off its account: the account's other peers no longer reach it, or list it. Its
|
|
80
|
+
* keys and pairings stay, and signing in again puts it back.
|
|
81
|
+
*/
|
|
82
|
+
disown(id: string): boolean {
|
|
83
|
+
return this.db.prepare("UPDATE peers SET user_id = NULL WHERE id = ? AND user_id IS NOT NULL").run(id).changes > 0;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
linkedAt(machineId: string, deviceId: string): number | undefined {
|
|
87
|
+
const row = this.db.prepare("SELECT created_at FROM links WHERE machine_id = ? AND device_id = ?").get(machineId, deviceId) as
|
|
88
|
+
| { created_at: number }
|
|
89
|
+
| undefined;
|
|
90
|
+
return row?.created_at;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/** Peers of the other role that `id` may reach: linked, or owned by the same account. */
|
|
94
|
+
reachable(id: string): { peer: PeerRecord; via: "paired" | "account"; pairedAt?: number }[] {
|
|
95
|
+
const self = this.peer(id);
|
|
96
|
+
if (!self) return [];
|
|
97
|
+
const other = self.role === "machine" ? "device" : "machine";
|
|
98
|
+
const linked = (
|
|
99
|
+
self.role === "machine"
|
|
100
|
+
? this.db.prepare("SELECT p.*, l.created_at AS paired_at FROM links l JOIN peers p ON p.id = l.device_id WHERE l.machine_id = ?").all(id)
|
|
101
|
+
: this.db.prepare("SELECT p.*, l.created_at AS paired_at FROM links l JOIN peers p ON p.id = l.machine_id WHERE l.device_id = ?").all(id)
|
|
102
|
+
) as Record<string, unknown>[];
|
|
103
|
+
const out = new Map<string, { peer: PeerRecord; via: "paired" | "account"; pairedAt?: number }>();
|
|
104
|
+
for (const row of linked) out.set(String(row.id), { peer: toPeer(row), via: "paired", pairedAt: Number(row.paired_at) });
|
|
105
|
+
if (self.userId) {
|
|
106
|
+
const owned = this.db.prepare("SELECT * FROM peers WHERE user_id = ? AND role = ?").all(self.userId, other) as Record<string, unknown>[];
|
|
107
|
+
for (const row of owned) if (!out.has(String(row.id))) out.set(String(row.id), { peer: toPeer(row), via: "account" });
|
|
108
|
+
}
|
|
109
|
+
return [...out.values()];
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/** How `from` may reach `to`, or undefined when it may not. */
|
|
113
|
+
route(from: string, to: string): "paired" | "account" | undefined {
|
|
114
|
+
const a = this.peer(from);
|
|
115
|
+
const b = this.peer(to);
|
|
116
|
+
if (!a || !b || a.role === b.role) return undefined;
|
|
117
|
+
const [machine, device] = a.role === "machine" ? [a, b] : [b, a];
|
|
118
|
+
if (this.linkedAt(machine.id, device.id) !== undefined) return "paired";
|
|
119
|
+
if (a.userId && a.userId === b.userId) return "account";
|
|
120
|
+
return undefined;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function toPeer(row: Record<string, unknown>): PeerRecord {
|
|
125
|
+
return {
|
|
126
|
+
id: String(row.id),
|
|
127
|
+
role: row.role as PeerRole,
|
|
128
|
+
signKey: String(row.sign_key),
|
|
129
|
+
boxKey: String(row.box_key),
|
|
130
|
+
name: String(row.name),
|
|
131
|
+
platform: row.platform ? String(row.platform) : undefined,
|
|
132
|
+
userId: row.user_id ? String(row.user_id) : undefined,
|
|
133
|
+
lastSeen: Number(row.last_seen),
|
|
134
|
+
};
|
|
135
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { GatewayOptions } from "./relay.js";
|
|
2
|
+
|
|
3
|
+
// The official gateway is a subscription service. Whether an account is Pro is
|
|
4
|
+
// read from iTool's `profiles` table, with the service-role key: the gateway
|
|
5
|
+
// asks on its own behalf, not with the user's token.
|
|
6
|
+
|
|
7
|
+
export interface SubscriptionCheck {
|
|
8
|
+
/** "unknown": the lookup itself failed, which says nothing about the account. */
|
|
9
|
+
status: "active" | "inactive" | "unknown";
|
|
10
|
+
reason?: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export type CheckSubscription = (userId: string) => Promise<SubscriptionCheck>;
|
|
14
|
+
|
|
15
|
+
export function supabaseSubscriptions(supabase: { url: string; anonKey: string; serviceRoleKey: string }): CheckSubscription {
|
|
16
|
+
return async (userId) => {
|
|
17
|
+
if (!supabase.url || !supabase.anonKey) return { status: "unknown", reason: "missing_supabase_config" };
|
|
18
|
+
if (!supabase.serviceRoleKey) return { status: "unknown", reason: "missing_service_role_key" };
|
|
19
|
+
try {
|
|
20
|
+
const response = await fetch(`${supabase.url}/rest/v1/profiles?id=eq.${userId}&select=plan,plan_expires_at&limit=1`, {
|
|
21
|
+
headers: { Authorization: `Bearer ${supabase.serviceRoleKey}`, apikey: supabase.anonKey },
|
|
22
|
+
signal: AbortSignal.timeout(5_000),
|
|
23
|
+
});
|
|
24
|
+
if (!response.ok) return { status: "unknown", reason: `profile_lookup_http_${response.status}` };
|
|
25
|
+
const profiles = (await response.json()) as { plan: string; plan_expires_at: string | null }[];
|
|
26
|
+
const profile = profiles[0];
|
|
27
|
+
if (!profile) return { status: "unknown", reason: "profile_not_found" };
|
|
28
|
+
const active = profile.plan === "pro" && !!profile.plan_expires_at && new Date(profile.plan_expires_at) > new Date();
|
|
29
|
+
return { status: active ? "active" : "inactive" };
|
|
30
|
+
} catch {
|
|
31
|
+
return { status: "unknown", reason: "profile_lookup_failed" };
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* The official gateway's rule for who may connect: a computer needs an active
|
|
38
|
+
* Pro account. Devices only reach computers they're paired with or share an
|
|
39
|
+
* account with, so they need nothing more.
|
|
40
|
+
*/
|
|
41
|
+
export function requirePro(check: CheckSubscription, warn: (message: string) => void): NonNullable<GatewayOptions["admit"]> {
|
|
42
|
+
return async ({ role, userId }) => {
|
|
43
|
+
if (role !== "machine") return undefined;
|
|
44
|
+
if (!userId) return "官方网关需要登录 Pro 账号:在电脑上运行 linkshell login";
|
|
45
|
+
const subscription = await check(userId);
|
|
46
|
+
// A lookup failure isn't a lapsed subscription; don't lock people out over it.
|
|
47
|
+
if (subscription.status === "inactive") return "官方网关需要 Pro 订阅:https://itool.tech";
|
|
48
|
+
if (subscription.status === "unknown") warn(`subscription check unavailable (${subscription.reason}); admitting ${userId}`);
|
|
49
|
+
return undefined;
|
|
50
|
+
};
|
|
51
|
+
}
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import type { IncomingMessage, ServerResponse } from "node:http";
|
|
2
|
-
declare const SUPABASE_URL: string;
|
|
3
|
-
declare const SUPABASE_ANON_KEY: string;
|
|
4
|
-
declare const SUPABASE_SERVICE_ROLE_KEY: string;
|
|
5
|
-
declare const AUTH_REQUIRED: boolean;
|
|
6
|
-
export { AUTH_REQUIRED, SUPABASE_URL, SUPABASE_ANON_KEY, SUPABASE_SERVICE_ROLE_KEY };
|
|
7
|
-
export interface AuthResult {
|
|
8
|
-
authenticated: boolean;
|
|
9
|
-
userId?: string;
|
|
10
|
-
email?: string;
|
|
11
|
-
plan?: string;
|
|
12
|
-
subscribed?: boolean;
|
|
13
|
-
}
|
|
14
|
-
export interface SubscriptionCheckResult {
|
|
15
|
-
status: "active" | "inactive" | "unknown";
|
|
16
|
-
reason?: string;
|
|
17
|
-
}
|
|
18
|
-
export declare function canReadSessionDetail(input: {
|
|
19
|
-
authRequired: boolean;
|
|
20
|
-
authenticatedUserId?: string;
|
|
21
|
-
sessionUserId?: string;
|
|
22
|
-
tokenOwns: boolean;
|
|
23
|
-
}): boolean;
|
|
24
|
-
/**
|
|
25
|
-
* Server-side subscription check using service role key (no user JWT needed).
|
|
26
|
-
* Returns "unknown" for configuration or network failures so active sessions
|
|
27
|
-
* are not disconnected because of a transient verification problem.
|
|
28
|
-
*/
|
|
29
|
-
export declare function checkSubscriptionByUserId(userId: string): Promise<SubscriptionCheckResult>;
|
|
30
|
-
export declare function extractAuthToken(req: IncomingMessage): string | null;
|
|
31
|
-
/**
|
|
32
|
-
* Validate token and return auth result (exported for /sessions/mine).
|
|
33
|
-
*/
|
|
34
|
-
export declare function validateRequest(req: IncomingMessage): Promise<AuthResult | null>;
|
|
35
|
-
/**
|
|
36
|
-
* Auth middleware for premium gateway HTTP endpoints.
|
|
37
|
-
* Rejects non-subscribers with a message pointing to itool.tech.
|
|
38
|
-
*/
|
|
39
|
-
export declare function requireAuth(req: IncomingMessage, res: ServerResponse): Promise<AuthResult | null>;
|
|
40
|
-
/**
|
|
41
|
-
* Check auth for WebSocket upgrade requests.
|
|
42
|
-
* Returns AuthResult or null if unauthorized.
|
|
43
|
-
*/
|
|
44
|
-
export declare function checkWsAuth(req: IncomingMessage): Promise<AuthResult | null>;
|
|
@@ -1,204 +0,0 @@
|
|
|
1
|
-
const SUPABASE_URL = process.env.SUPABASE_URL ?? "";
|
|
2
|
-
const SUPABASE_ANON_KEY = process.env.SUPABASE_ANON_KEY ?? "";
|
|
3
|
-
const SUPABASE_SERVICE_ROLE_KEY = process.env.SUPABASE_SERVICE_ROLE_KEY ?? "";
|
|
4
|
-
const AUTH_REQUIRED = process.env.AUTH_REQUIRED === "true";
|
|
5
|
-
export { AUTH_REQUIRED, SUPABASE_URL, SUPABASE_ANON_KEY, SUPABASE_SERVICE_ROLE_KEY };
|
|
6
|
-
export function canReadSessionDetail(input) {
|
|
7
|
-
if (input.tokenOwns)
|
|
8
|
-
return true;
|
|
9
|
-
return Boolean(input.authRequired &&
|
|
10
|
-
input.authenticatedUserId &&
|
|
11
|
-
input.sessionUserId &&
|
|
12
|
-
input.authenticatedUserId === input.sessionUserId);
|
|
13
|
-
}
|
|
14
|
-
const tokenAuthCache = new Map();
|
|
15
|
-
const TOKEN_CACHE_TTL = 45_000; // 45s
|
|
16
|
-
function getCachedAuth(token) {
|
|
17
|
-
const entry = tokenAuthCache.get(token);
|
|
18
|
-
if (!entry)
|
|
19
|
-
return null;
|
|
20
|
-
if (Date.now() > entry.expiresAt) {
|
|
21
|
-
tokenAuthCache.delete(token);
|
|
22
|
-
return null;
|
|
23
|
-
}
|
|
24
|
-
return entry.result;
|
|
25
|
-
}
|
|
26
|
-
function setCachedAuth(token, result) {
|
|
27
|
-
// Only cache successful authentications — never pin a failure.
|
|
28
|
-
if (!result.authenticated)
|
|
29
|
-
return;
|
|
30
|
-
tokenAuthCache.set(token, { result, expiresAt: Date.now() + TOKEN_CACHE_TTL });
|
|
31
|
-
if (tokenAuthCache.size > 1000) {
|
|
32
|
-
const now = Date.now();
|
|
33
|
-
for (const [k, v] of tokenAuthCache) {
|
|
34
|
-
if (now > v.expiresAt)
|
|
35
|
-
tokenAuthCache.delete(k);
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
/**
|
|
40
|
-
* Validate a Supabase JWT and check subscription via iTool's profiles table.
|
|
41
|
-
*/
|
|
42
|
-
async function validateToken(token) {
|
|
43
|
-
if (!SUPABASE_URL || !SUPABASE_ANON_KEY) {
|
|
44
|
-
return { authenticated: false };
|
|
45
|
-
}
|
|
46
|
-
const cached = getCachedAuth(token);
|
|
47
|
-
if (cached)
|
|
48
|
-
return cached;
|
|
49
|
-
try {
|
|
50
|
-
const res = await fetch(`${SUPABASE_URL}/auth/v1/user`, {
|
|
51
|
-
headers: {
|
|
52
|
-
Authorization: `Bearer ${token}`,
|
|
53
|
-
apikey: SUPABASE_ANON_KEY,
|
|
54
|
-
},
|
|
55
|
-
signal: AbortSignal.timeout(5_000),
|
|
56
|
-
});
|
|
57
|
-
if (!res.ok)
|
|
58
|
-
return { authenticated: false };
|
|
59
|
-
const user = (await res.json());
|
|
60
|
-
// Check subscription from iTool's profiles table
|
|
61
|
-
let plan = "free";
|
|
62
|
-
let subscribed = false;
|
|
63
|
-
try {
|
|
64
|
-
const profileRes = await fetch(`${SUPABASE_URL}/rest/v1/profiles?id=eq.${user.id}&select=plan,plan_expires_at&limit=1`, {
|
|
65
|
-
headers: {
|
|
66
|
-
Authorization: `Bearer ${token}`,
|
|
67
|
-
apikey: SUPABASE_ANON_KEY,
|
|
68
|
-
},
|
|
69
|
-
signal: AbortSignal.timeout(5_000),
|
|
70
|
-
});
|
|
71
|
-
if (profileRes.ok) {
|
|
72
|
-
const profiles = (await profileRes.json());
|
|
73
|
-
if (profiles.length > 0) {
|
|
74
|
-
const p = profiles[0];
|
|
75
|
-
plan = p.plan;
|
|
76
|
-
subscribed =
|
|
77
|
-
p.plan === "pro" &&
|
|
78
|
-
!!p.plan_expires_at &&
|
|
79
|
-
new Date(p.plan_expires_at) > new Date();
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
catch { }
|
|
84
|
-
const authResult = {
|
|
85
|
-
authenticated: true,
|
|
86
|
-
userId: user.id,
|
|
87
|
-
email: user.email,
|
|
88
|
-
plan,
|
|
89
|
-
subscribed,
|
|
90
|
-
};
|
|
91
|
-
setCachedAuth(token, authResult);
|
|
92
|
-
return authResult;
|
|
93
|
-
}
|
|
94
|
-
catch {
|
|
95
|
-
return { authenticated: false };
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
/**
|
|
99
|
-
* Server-side subscription check using service role key (no user JWT needed).
|
|
100
|
-
* Returns "unknown" for configuration or network failures so active sessions
|
|
101
|
-
* are not disconnected because of a transient verification problem.
|
|
102
|
-
*/
|
|
103
|
-
export async function checkSubscriptionByUserId(userId) {
|
|
104
|
-
if (!SUPABASE_URL || !SUPABASE_ANON_KEY) {
|
|
105
|
-
return { status: "unknown", reason: "missing_supabase_config" };
|
|
106
|
-
}
|
|
107
|
-
if (!SUPABASE_SERVICE_ROLE_KEY) {
|
|
108
|
-
return { status: "unknown", reason: "missing_service_role_key" };
|
|
109
|
-
}
|
|
110
|
-
try {
|
|
111
|
-
const res = await fetch(`${SUPABASE_URL}/rest/v1/profiles?id=eq.${userId}&select=plan,plan_expires_at&limit=1`, {
|
|
112
|
-
headers: {
|
|
113
|
-
Authorization: `Bearer ${SUPABASE_SERVICE_ROLE_KEY}`,
|
|
114
|
-
apikey: SUPABASE_ANON_KEY,
|
|
115
|
-
},
|
|
116
|
-
signal: AbortSignal.timeout(5_000),
|
|
117
|
-
});
|
|
118
|
-
if (!res.ok)
|
|
119
|
-
return { status: "unknown", reason: `profile_lookup_http_${res.status}` };
|
|
120
|
-
const profiles = (await res.json());
|
|
121
|
-
if (profiles.length === 0)
|
|
122
|
-
return { status: "unknown", reason: "profile_not_found" };
|
|
123
|
-
const p = profiles[0];
|
|
124
|
-
const active = p.plan === "pro" &&
|
|
125
|
-
!!p.plan_expires_at &&
|
|
126
|
-
new Date(p.plan_expires_at) > new Date();
|
|
127
|
-
return { status: active ? "active" : "inactive" };
|
|
128
|
-
}
|
|
129
|
-
catch {
|
|
130
|
-
return { status: "unknown", reason: "profile_lookup_failed" };
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
export function extractAuthToken(req) {
|
|
134
|
-
const url = new URL(req.url ?? "/", "http://localhost");
|
|
135
|
-
const token = url.searchParams.get("auth_token");
|
|
136
|
-
if (token)
|
|
137
|
-
return token;
|
|
138
|
-
const auth = req.headers.authorization;
|
|
139
|
-
if (auth) {
|
|
140
|
-
const match = auth.match(/^Bearer\s+(.+)$/i);
|
|
141
|
-
if (match?.[1])
|
|
142
|
-
return match[1];
|
|
143
|
-
}
|
|
144
|
-
return null;
|
|
145
|
-
}
|
|
146
|
-
/**
|
|
147
|
-
* Validate token and return auth result (exported for /sessions/mine).
|
|
148
|
-
*/
|
|
149
|
-
export async function validateRequest(req) {
|
|
150
|
-
const token = extractAuthToken(req);
|
|
151
|
-
if (!token)
|
|
152
|
-
return null;
|
|
153
|
-
const result = await validateToken(token);
|
|
154
|
-
return result.authenticated ? result : null;
|
|
155
|
-
}
|
|
156
|
-
/**
|
|
157
|
-
* Auth middleware for premium gateway HTTP endpoints.
|
|
158
|
-
* Rejects non-subscribers with a message pointing to itool.tech.
|
|
159
|
-
*/
|
|
160
|
-
export async function requireAuth(req, res) {
|
|
161
|
-
if (!AUTH_REQUIRED) {
|
|
162
|
-
return { authenticated: true };
|
|
163
|
-
}
|
|
164
|
-
const token = extractAuthToken(req);
|
|
165
|
-
if (!token) {
|
|
166
|
-
res.writeHead(401, { "Content-Type": "application/json" });
|
|
167
|
-
res.end(JSON.stringify({ error: "auth_required", message: "Authentication required" }));
|
|
168
|
-
return null;
|
|
169
|
-
}
|
|
170
|
-
const result = await validateToken(token);
|
|
171
|
-
if (!result.authenticated) {
|
|
172
|
-
res.writeHead(401, { "Content-Type": "application/json" });
|
|
173
|
-
res.end(JSON.stringify({ error: "invalid_token", message: "Invalid or expired token" }));
|
|
174
|
-
return null;
|
|
175
|
-
}
|
|
176
|
-
if (!result.subscribed) {
|
|
177
|
-
res.writeHead(403, { "Content-Type": "application/json" });
|
|
178
|
-
res.end(JSON.stringify({
|
|
179
|
-
error: "subscription_required",
|
|
180
|
-
message: "Pro subscription required. Subscribe at https://itool.tech",
|
|
181
|
-
}));
|
|
182
|
-
return null;
|
|
183
|
-
}
|
|
184
|
-
return result;
|
|
185
|
-
}
|
|
186
|
-
/**
|
|
187
|
-
* Check auth for WebSocket upgrade requests.
|
|
188
|
-
* Returns AuthResult or null if unauthorized.
|
|
189
|
-
*/
|
|
190
|
-
export async function checkWsAuth(req) {
|
|
191
|
-
if (!AUTH_REQUIRED) {
|
|
192
|
-
return { authenticated: true };
|
|
193
|
-
}
|
|
194
|
-
const token = extractAuthToken(req);
|
|
195
|
-
if (!token)
|
|
196
|
-
return null;
|
|
197
|
-
const result = await validateToken(token);
|
|
198
|
-
if (!result.authenticated)
|
|
199
|
-
return null;
|
|
200
|
-
if (!result.subscribed)
|
|
201
|
-
return null;
|
|
202
|
-
return result;
|
|
203
|
-
}
|
|
204
|
-
//# sourceMappingURL=auth-middleware.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"auth-middleware.js","sourceRoot":"","sources":["../../../src/auth-middleware.ts"],"names":[],"mappings":"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"}
|
package/dist/gateway/src/cors.js
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
export function setCors(res) {
|
|
2
|
-
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
3
|
-
res.setHeader("Access-Control-Allow-Methods", "GET, POST, DELETE, OPTIONS");
|
|
4
|
-
res.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization");
|
|
5
|
-
res.setHeader("Access-Control-Max-Age", "86400");
|
|
6
|
-
}
|
|
7
|
-
//# sourceMappingURL=cors.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"cors.js","sourceRoot":"","sources":["../../../src/cors.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,OAAO,CAAC,GAAmB;IACzC,GAAG,CAAC,SAAS,CAAC,6BAA6B,EAAE,GAAG,CAAC,CAAC;IAClD,GAAG,CAAC,SAAS,CAAC,8BAA8B,EAAE,4BAA4B,CAAC,CAAC;IAC5E,GAAG,CAAC,SAAS,CAAC,8BAA8B,EAAE,6BAA6B,CAAC,CAAC;IAC7E,GAAG,CAAC,SAAS,CAAC,wBAAwB,EAAE,OAAO,CAAC,CAAC;AACnD,CAAC"}
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
export interface EmbeddedGatewayOptions {
|
|
2
|
-
port?: number;
|
|
3
|
-
logLevel?: "debug" | "info" | "warn" | "error";
|
|
4
|
-
silent?: boolean;
|
|
5
|
-
/**
|
|
6
|
-
* Also serve the v2 relay (end-to-end encrypted, at /v2/connect), keeping its
|
|
7
|
-
* pairings in SQLite here. Needs Node.js 22.13+ (node:sqlite).
|
|
8
|
-
*/
|
|
9
|
-
relayDataPath?: string;
|
|
10
|
-
}
|
|
11
|
-
export interface EmbeddedGateway {
|
|
12
|
-
port: number;
|
|
13
|
-
httpUrl: string;
|
|
14
|
-
wsUrl: string;
|
|
15
|
-
close: () => Promise<void>;
|
|
16
|
-
}
|
|
17
|
-
/**
|
|
18
|
-
* Start an embedded gateway. Returns a handle to get URLs and close it.
|
|
19
|
-
* Used by CLI when no external --gateway is provided.
|
|
20
|
-
*/
|
|
21
|
-
export declare function startEmbeddedGateway(options?: EmbeddedGatewayOptions): Promise<EmbeddedGateway>;
|