diff --git a/deno.jsonc b/deno.jsonc index 051d2f0..75b4764 100644 --- a/deno.jsonc +++ b/deno.jsonc @@ -1,6 +1,6 @@ { "name": "@starfiles/hydrafiles", - "version": "0.7.34", + "version": "0.7.35", "description": "The (P2P) web privacy layer.", "main": "src/hydrafiles.ts", "exports": { diff --git a/src/hydrafiles.ts b/src/hydrafiles.ts index 7fd92f9..459abfa 100644 --- a/src/hydrafiles.ts +++ b/src/hydrafiles.ts @@ -49,11 +49,9 @@ class Hydrafiles { console.log("Startup: Populating FileDB"); this.fileDB = await FileDB.init(this); console.log("Startup: Populating RPC Client & Server"); - this.rpcClient = new RPCClient(this); - this.rpcClient.start().then(() => { - this.rpcServer = new RPCServer(this); - this.startBackgroundTasks(onUpdateFileListProgress); - }); + this.rpcClient = await RPCClient.init(this); + this.rpcServer = new RPCServer(this); + this.startBackgroundTasks(onUpdateFileListProgress); } startBackgroundTasks(onUpdateFileListProgress?: (progress: number, total: number) => void): void { @@ -164,7 +162,7 @@ class Hydrafiles { (await this.fs.readDir("files/")).length, `(${Math.round((100 * await this.utils.calculateUsedStorage()) / 1024 / 1024 / 1024) / 100}GB)`, "\n| Processing Files:", - this.rpcServer.hashLocks.size, + this.rpcServer.processingRequests.size, "\n| Known HTTP Peers:", (await this.rpcClient.http.getPeers()).length, // '\n| Seeding Torrent Files:', diff --git a/src/rpc/client.ts b/src/rpc/client.ts index 495c979..fe6d794 100644 --- a/src/rpc/client.ts +++ b/src/rpc/client.ts @@ -5,16 +5,18 @@ import File from "../file.ts"; import Utils, { type Sha256 } from "../utils.ts"; export default class RPCClient { - private _client: Hydrafiles; + _client: Hydrafiles; http!: HTTPClient; rtc!: RTCClient; - constructor(client: Hydrafiles) { + private constructor(client: Hydrafiles) { this._client = client; } - async start(): Promise { - this.http = await HTTPClient.init(this._client); - this.rtc = await RTCClient.init(this._client); + static async init(client: Hydrafiles): Promise { + const rpcClient = new RPCClient(client); + rpcClient.http = await HTTPClient.init(rpcClient); + rpcClient.rtc = await RTCClient.init(rpcClient); + return rpcClient; } public async fetch(input: RequestInfo, init?: RequestInit): Promise[]> { @@ -37,7 +39,7 @@ export default class RPCClient { for (const peer of peers) { let fileContent: { file: Uint8Array; signal: number } | false = false; try { - fileContent = await this.http.downloadFromPeer(await HTTPPeer.init(peer, this.http._db), file); + fileContent = await (await HTTPPeer.init(peer, this.http.db, this._client)).downloadFile(file); } catch (e) { console.error(e); } diff --git a/src/rpc/peers/http.ts b/src/rpc/peers/http.ts index b320879..afecdf0 100644 --- a/src/rpc/peers/http.ts +++ b/src/rpc/peers/http.ts @@ -1,17 +1,18 @@ import type Hydrafiles from "../../hydrafiles.ts"; -import Utils from "../../utils.ts"; +import Utils, { type NonNegativeNumber } from "../../utils.ts"; import type { Database } from "jsr:@db/sqlite@0.11"; import type { indexedDB } from "https://deno.land/x/indexeddb@v1.1.0/ponyfill.ts"; import File from "../../file.ts"; +import type RPCClient from "../client.ts"; type DatabaseWrapper = { type: "UNDEFINED"; db: undefined } | { type: "SQLITE"; db: Database } | { type: "INDEXEDDB"; db: IDBDatabase }; interface PeerAttributes { host: string; - hits: number; - rejects: number; - bytes: number; - duration: number; + hits: NonNegativeNumber; + rejects: NonNegativeNumber; + bytes: NonNegativeNumber; + duration: NonNegativeNumber; updatedAt: string; } @@ -51,12 +52,15 @@ function createIDBDatabase(): Promise { return dbPromise; } -export class PeerDB { - private _client: Hydrafiles; +/** + * @group Database + */ +class PeerDB { + private _rpcClient: RPCClient; db: DatabaseWrapper = { type: "UNDEFINED", db: undefined }; - private constructor(client: Hydrafiles) { - this._client = client; + private constructor(rpcClient: RPCClient) { + this._rpcClient = rpcClient; } /** @@ -64,8 +68,8 @@ export class PeerDB { * @returns {PeerDB} A new instance of PeerDB. * @default */ - static async init(client: Hydrafiles): Promise { - const peerDB = new PeerDB(client); + static async init(rpcClient: RPCClient): Promise { + const peerDB = new PeerDB(rpcClient); if (typeof window === "undefined") { const database = (await import("jsr:@db/sqlite@0.11")).Database; @@ -224,16 +228,16 @@ export class PeerDB { const query = `UPDATE peer SET ${updatedColumn.map((column) => `${column} = ?`).join(", ")} WHERE host = ?`; this.db.db.prepare(query).values(params); console.log( - ` ${host} Peer UPDATEd - Updated Columns: ${updatedColumn.join(", ")}` + (this._client.config.logLevel === "verbose" ? ` - Params: ${params.join(", ")} - Query: ${query}` : ""), - this._client.config.logLevel === "verbose" ? console.log(` ${host} Updated Values:`, beforeAndAfter) : "", + ` ${host} Peer UPDATEd - Updated Columns: ${updatedColumn.join(", ")}` + (this._rpcClient._client.config.logLevel === "verbose" ? ` - Params: ${params.join(", ")} - Query: ${query}` : ""), + this._rpcClient._client.config.logLevel === "verbose" ? console.log(` ${host} Updated Values:`, beforeAndAfter) : "", ); } else { // @ts-expect-error: const { _db, ...clonedPeer } = newPeer; if (this.db.type === "INDEXEDDB") this.objectStore().put(clonedPeer).onerror = console.error; console.log( - ` ${host} Peer UPDATEd - Updated Columns: ${updatedColumn.join(", ")}` + (this._client.config.logLevel === "verbose" ? ` - Params: ${params.join(", ")}` : ""), - this._client.config.logLevel === "verbose" ? console.log(` ${host} Updated Values:`, beforeAndAfter) : "", + ` ${host} Peer UPDATEd - Updated Columns: ${updatedColumn.join(", ")}` + (this._rpcClient._client.config.logLevel === "verbose" ? ` - Params: ${params.join(", ")}` : ""), + this._rpcClient._client.config.logLevel === "verbose" ? console.log(` ${host} Updated Values:`, beforeAndAfter) : "", ); } } @@ -314,14 +318,16 @@ export class PeerDB { export class HTTPPeer implements PeerAttributes { host: string; - hits = 0; - rejects = 0; - bytes = 0; - duration = 0; + hits: NonNegativeNumber = 0 as NonNegativeNumber; + rejects: NonNegativeNumber = 0 as NonNegativeNumber; + bytes: NonNegativeNumber = 0 as NonNegativeNumber; + duration: NonNegativeNumber = 0 as NonNegativeNumber; updatedAt: string = new Date().toISOString(); private _db: PeerDB; + private _client: Hydrafiles; - private constructor(values: PeerAttributes, db: PeerDB) { + private constructor(values: PeerAttributes, db: PeerDB, client: Hydrafiles) { + this._client = client; this._db = db; if (values.host === undefined || values.host === null) throw new Error("Created peer without host"); @@ -339,7 +345,7 @@ export class HTTPPeer implements PeerAttributes { * @returns {HTTPPeer} A new instance of HTTPPeer. * @default */ - static async init(values: Partial, db: PeerDB): Promise { + static async init(values: Partial, db: PeerDB, client: Hydrafiles): Promise { if (values.host === undefined) throw new Error("Hash is required"); const peerAttributes: PeerAttributes = { host: values.host, @@ -356,68 +362,23 @@ export class HTTPPeer implements PeerAttributes { peer = (await db.select({ key: "host", value: values.host }))[0]; } - return new HTTPPeer(peer, db); + return new HTTPPeer(peer, db, client); } save(): void { this.updatedAt = new Date().toISOString(); if (this._db) this._db.update(this.host, this); } -} - -// TODO: Log common user-agents and re-use them to help anonimise non Hydrafiles peers -export default class HTTPClient { - private _client: Hydrafiles; - /** @internal */ - public _db: PeerDB; - private constructor(client: Hydrafiles, db: PeerDB) { - this._client = client; - this._db = db; - } - - /** - * Initializes an instance of HTTPClient. - * @returns {HTTPClient} A new instance of HTTPClient. - * @default - */ - public static async init(client: Hydrafiles): Promise { - const db = await PeerDB.init(client); - const peers = new HTTPClient(client, db); - - for (let i = 0; i < client.config.bootstrapPeers.length; i++) { - await peers.add(client.config.bootstrapPeers[i]); - } - return peers; - } - - async add(host: string): Promise { - if (host !== this._client.config.publicHostname) await HTTPPeer.init({ host }, this._db); - } - - public getPeers = async (applicablePeers = false): Promise => { - const peers = (await this._db.select()).filter((peer) => !applicablePeers || typeof window === "undefined" || !peer.host.startsWith("http://")); - - if (this._client.config.preferNode === "FASTEST") { - return peers.sort((a, b) => a.bytes / a.duration - b.bytes / b.duration); - } else if (this._client.config.preferNode === "LEAST_USED") { - return peers.sort((a, b) => a.hits - a.rejects - (b.hits - b.rejects)); - } else if (this._client.config.preferNode === "HIGHEST_HITRATE") { - return peers.sort((a, b) => a.hits - a.rejects - (b.hits - b.rejects)); - } else { - return peers; - } - }; - - async downloadFromPeer(peer: HTTPPeer, file: File): Promise<{ file: Uint8Array; signal: number } | false> { + async downloadFile(file: File): Promise<{ file: Uint8Array; signal: number } | false> { try { const startTime = Date.now(); const hash = file.hash; - console.log(` ${hash} Downloading from ${peer.host}`); + console.log(` ${hash} Downloading from ${this.host}`); let response; try { - response = await Utils.promiseWithTimeout(fetch(`${peer.host}/download/${hash}`), this._client.config.timeout); + response = await Utils.promiseWithTimeout(fetch(`${this.host}/download/${hash}`), this._client.config.timeout); } catch (e) { const err = e as Error; if (this._client.config.logLevel === "verbose" && err.message !== "Promise timed out") console.error(e); @@ -435,10 +396,10 @@ export default class HTTPClient { file.save(); } - peer.duration += Date.now() - startTime; - peer.bytes += peerContent.byteLength; - peer.hits++; - peer.save(); + this.duration = Utils.createNonNegativeNumber(this.duration + Date.now() - startTime); + this.bytes = Utils.createNonNegativeNumber(this.bytes + peerContent.byteLength); + this.hits++; + this.save(); await file.cacheFile(peerContent); return { @@ -447,12 +408,56 @@ export default class HTTPClient { }; } catch (e) { console.error(e); - peer.rejects++; + this.rejects++; - peer.save(); + this.save(); return false; } } +} + +// TODO: Log common user-agents and re-use them to help anonimise non Hydrafiles peers +export default class HTTPClient { + private _rpcClient: RPCClient; + public db: PeerDB; + + private constructor(rpcClient: RPCClient, db: PeerDB) { + this._rpcClient = rpcClient; + this.db = db; + } + + /** + * Initializes an instance of HTTPClient. + * @returns {HTTPClient} A new instance of HTTPClient. + * @default + */ + public static async init(rpcClient: RPCClient): Promise { + const db = await PeerDB.init(rpcClient); + const peers = new HTTPClient(rpcClient, db); + + for (let i = 0; i < rpcClient._client.config.bootstrapPeers.length; i++) { + await peers.add(rpcClient._client.config.bootstrapPeers[i]); + } + return peers; + } + + async add(host: string): Promise { + if (host !== this._rpcClient._client.config.publicHostname) await HTTPPeer.init({ host }, this.db, this._rpcClient._client); + } + + public getPeers = async (applicablePeers = false): Promise => { + const peers = (await this.db.select()).filter((peer) => !applicablePeers || typeof window === "undefined" || !peer.host.startsWith("http://")); + + if (this._rpcClient._client.config.preferNode === "FASTEST") { + return peers.sort((a, b) => a.bytes / a.duration - b.bytes / b.duration); + } else if (this._rpcClient._client.config.preferNode === "LEAST_USED") { + return peers.sort((a, b) => a.hits - a.rejects - (b.hits - b.rejects)); + } else if (this._rpcClient._client.config.preferNode === "HIGHEST_HITRATE") { + return peers.sort((a, b) => a.hits - a.rejects - (b.hits - b.rejects)); + } else { + return peers; + } + }; async getValidPeers(): Promise { const peers = await this.getPeers(); @@ -461,11 +466,11 @@ export default class HTTPClient { for (let i = 0; i < peers.length; i++) { const peer = peers[i]; - if (peer.host === this._client.config.publicHostname) { + if (peer.host === this._rpcClient._client.config.publicHostname) { results.push(peer); continue; } - const promise = this.validatePeer(await HTTPPeer.init(peer, this._db)).then((result) => { + const promise = this.validatePeer(await HTTPPeer.init(peer, this.db, this._rpcClient._client)).then((result) => { if (result) results.push(peer); executing.splice(executing.indexOf(promise), 1); }); @@ -476,9 +481,9 @@ export default class HTTPClient { } async validatePeer(peer: HTTPPeer): Promise { - const file = await File.init({ hash: Utils.sha256("04aa07009174edc6f03224f003a435bcdc9033d2c52348f3a35fbb342ea82f6f") }, this._client); + const file = await File.init({ hash: Utils.sha256("04aa07009174edc6f03224f003a435bcdc9033d2c52348f3a35fbb342ea82f6f") }, this._rpcClient._client); if (!file) throw new Error("Failed to build file"); - return await this.downloadFromPeer(peer, file) !== false; + return await peer.downloadFile(file) !== false; } public async fetch(input: RequestInfo, init?: RequestInit): Promise[]> { @@ -490,9 +495,9 @@ export default class HTTPClient { const peerUrl = new URL(peer.host); url.hostname = peerUrl.hostname; url.protocol = peerUrl.protocol; - return await Utils.promiseWithTimeout(fetch(url.toString(), init), this._client.config.timeout); + return await Utils.promiseWithTimeout(fetch(url.toString(), init), this._rpcClient._client.config.timeout); } catch (e) { - if (this._client.config.logLevel === "verbose") console.error(e); + if (this._rpcClient._client.config.logLevel === "verbose") console.error(e); return false; } }); @@ -503,7 +508,7 @@ export default class HTTPClient { // TODO: Compare list between all peers and give score based on how similar they are. 100% = all exactly the same, 0% = no items in list were shared. The lower the score, the lower the propagation times, the lower the decentralisation async updatePeers(): Promise { console.log(`Fetching peers`); - const responses = await Promise.all(await this._client.rpcClient.fetch("http://localhost/peers")); + const responses = await Promise.all(await this._rpcClient._client.rpcClient.fetch("http://localhost/peers")); for (let i = 0; i < responses.length; i++) { try { if (!(responses[i] instanceof Response)) continue; @@ -512,12 +517,12 @@ export default class HTTPClient { const remotePeers = (await response.json()) as HTTPPeer[]; for (const remotePeer of remotePeers) { this.add(remotePeer.host).catch((e) => { - if (this._client.config.logLevel === "verbose") console.error(e); + if (this._rpcClient._client.config.logLevel === "verbose") console.error(e); }); } } } catch (e) { - if (this._client.config.logLevel === "verbose") console.error(e); + if (this._rpcClient._client.config.logLevel === "verbose") console.error(e); } } } diff --git a/src/rpc/peers/rtc.ts b/src/rpc/peers/rtc.ts index 62165c4..99c4ab1 100644 --- a/src/rpc/peers/rtc.ts +++ b/src/rpc/peers/rtc.ts @@ -1,5 +1,6 @@ import type { RTCDataChannel, RTCIceCandidate, RTCPeerConnection, RTCSessionDescription } from "npm:werift"; import type Hydrafiles from "../../hydrafiles.ts"; +import type RPCClient from "../client.ts"; function extractIPAddress(sdp: string): string { const ipv4Regex = /c=IN IP4 (\d{1,3}(?:\.\d{1,3}){3})/g; @@ -16,7 +17,12 @@ function extractIPAddress(sdp: string): string { return ipAddresses.filter((ip) => ip !== "0.0.0.0")[0] ?? ipAddresses[0]; } -export type Message = { announce: true; from: number } | { offer: RTCSessionDescription; from: number; to: number } | { answer: RTCSessionDescription; from: number; to: number } | { iceCandidate: RTCIceCandidate; from: number; to: number }; +export type SignallingAnnounce = { announce: true; from: number }; +export type SignallingOffer = { offer: RTCSessionDescription; from: number; to: number }; +export type SignallingAnswer = { answer: RTCSessionDescription; from: number; to: number }; +export type SignallingIceCandidate = { iceCandidate: RTCIceCandidate; from: number; to: number }; +export type SignallingMessage = SignallingAnnounce | SignallingOffer | SignallingAnswer | SignallingIceCandidate; + type PeerConnection = { conn: RTCPeerConnection; channel: RTCDataChannel }; type PeerConnections = { [id: string]: { offered?: PeerConnection; answered?: PeerConnection } }; @@ -36,15 +42,15 @@ function arrayBufferToUnicodeString(buffer: ArrayBuffer): string { const peerId = Math.random(); class RTCClient { - private _client: Hydrafiles; + private _rpcClient: RPCClient; peerId: number; websockets: WebSocket[]; peerConnections: PeerConnections = {}; - messageQueue: Message[] = []; + messageQueue: SignallingMessage[] = []; seenMessages: Set; - private constructor(client: Hydrafiles) { - this._client = client; + private constructor(rpcClient: RPCClient) { + this._rpcClient = rpcClient; this.peerId = peerId; this.websockets = [new WebSocket("wss://rooms.deno.dev/")]; this.seenMessages = new Set(); @@ -55,14 +61,14 @@ class RTCClient { * @returns {RTCClient} A new instance of RTCClient. * @default */ - static async init(client: Hydrafiles): Promise { - const webRTC = new RTCClient(client); - const peers = await client.rpcClient.http.getPeers(true); + static async init(rpcClient: RPCClient): Promise { + const webRTC = new RTCClient(rpcClient); + const peers = await rpcClient.http.getPeers(true); for (let i = 0; i < peers.length; i++) { try { webRTC.websockets.push(new WebSocket(peers[i].host.replace("https://", "wss://").replace("http://", "ws://"))); } catch (e) { - if (client.config.logLevel === "verbose") console.error(e); + if (rpcClient._client.config.logLevel === "verbose") console.error(e); continue; } } @@ -70,13 +76,13 @@ class RTCClient { for (let i = 0; i < webRTC.websockets.length; i++) { webRTC.websockets[i].onopen = () => { console.log(`WebRTC: (1/12): Announcing to ${webRTC.websockets[i].url}`); - const message: Message = { announce: true, from: webRTC.peerId }; + const message: SignallingMessage = { announce: true, from: webRTC.peerId }; webRTC.wsMessage(message); - setInterval(() => webRTC.wsMessage(message), client.config.announceSpeed); + setInterval(() => webRTC.wsMessage(message), rpcClient._client.config.announceSpeed); }; webRTC.websockets[i].onmessage = async (event) => { - const message = JSON.parse(event.data) as Message; + const message = JSON.parse(event.data) as SignallingMessage; if (message === null || message.from === peerId || webRTC.seenMessages.has(event.data) || ("to" in message && message.to !== webRTC.peerId)) return; webRTC.seenMessages.add(event.data); if ("announce" in message) await webRTC.handleAnnounce(message.from); @@ -110,7 +116,7 @@ class RTCClient { console.log(`WebRTC: (10/12): Received request`); const { id, url, ...data } = JSON.parse(e.data as string); const req = new Request(url, data); - const response = await this._client.rpcServer.handleRequest(req); + const response = await this._rpcClient._client.rpcServer.handleRequest(req); const headersObj: Record = {}; response.headers.forEach((value, key) => { headersObj[key] = value; @@ -169,7 +175,7 @@ class RTCClient { } } - wsMessage(message: Message): void { + wsMessage(message: SignallingMessage): void { this.messageQueue.push(message); for (let i = 0; i < this.websockets.length; i++) { if (this.websockets[i].readyState === 1) this.websockets[i].send(JSON.stringify(message)); diff --git a/src/rpc/server.ts b/src/rpc/server.ts index 8f3c777..0649e5f 100644 --- a/src/rpc/server.ts +++ b/src/rpc/server.ts @@ -6,14 +6,14 @@ import Utils from "../utils.ts"; import { join } from "https://deno.land/std@0.224.0/path/mod.ts"; import type Base64 from "npm:base64"; import { HTTPPeer } from "./peers/http.ts"; -import { Message } from "./peers/rtc.ts"; +import { SignallingMessage } from "./peers/rtc.ts"; import { serveFile } from "https://deno.land/std@0.115.0/http/file_server.ts"; class RPCServer { private _client: Hydrafiles; cachedHostnames: { [key: string]: { body: string; headers: Headers } } = {}; sockets: { id: number; socket: WebSocket }[] = []; - public hashLocks = new Map>(); + public processingRequests = new Map>(); public handleCustomRequest?: (req: Request) => Promise; constructor(client: Hydrafiles) { @@ -40,7 +40,7 @@ class RPCServer { } } - onListen = async (hostname: string, port: number): Promise => { + private onListen = async (hostname: string, port: number): Promise => { console.log(`Server started at ${hostname}:${port}`); console.log("Testing network connection"); const file = await this._client.rpcClient.downloadFile(Utils.sha256("04aa07009174edc6f03224f003a435bcdc9033d2c52348f3a35fbb342ea82f6f")); @@ -53,7 +53,7 @@ class RPCServer { const file = await File.init({ hash: Utils.sha256("04aa07009174edc6f03224f003a435bcdc9033d2c52348f3a35fbb342ea82f6f") }, this._client); if (!file) console.error("Failed to build file"); else { - const response = await this._client.rpcClient.http.downloadFromPeer(await HTTPPeer.init({ host: this._client.config.publicHostname }, this._client.rpcClient.http._db), file); + const response = await (await HTTPPeer.init({ host: this._client.config.publicHostname }, this._client.rpcClient.http.db, this._client)).downloadFile(file); if (response === false) console.error("Test: Failed to download file from self"); else { console.log("Announcing HTTP server to nodes"); @@ -77,7 +77,7 @@ class RPCServer { this.sockets.push({ socket, id: 0 }); socket.addEventListener("message", ({ data }) => { - const message = JSON.parse(data) as Message | null; + const message = JSON.parse(data) as SignallingMessage | null; if (message === null) return; for (let i = 0; i < this.sockets.length; i++) { if (this.sockets[i].socket !== socket && (!("to" in message) || message.to === this.sockets[i].id)) { @@ -120,9 +120,9 @@ class RPCServer { const fileId = url.pathname.split("/")[3] ?? ""; const infohash = Array.from(decodeURIComponent(url.searchParams.get("info_hash") ?? "")).map((char) => char.charCodeAt(0).toString(16).padStart(2, "0")).join(""); - if (this.hashLocks.has(hash)) { + if (this.processingRequests.has(hash)) { if (this._client.config.logLevel === "verbose") console.log(` ${hash} Waiting for existing request with same hash`); - await this.hashLocks.get(hash); + await this.processingRequests.get(hash); } const processingPromise = (async () => { const file = await File.init({ hash, infohash }, this._client, true); @@ -171,21 +171,21 @@ class RPCServer { return new Response(fileContent.file, { headers }); })(); - this.hashLocks.set(hash, processingPromise); + this.processingRequests.set(hash, processingPromise); let response: Response; try { response = await processingPromise; } finally { - this.hashLocks.delete(hash); + this.processingRequests.delete(hash); } return response; } else if (url.pathname?.startsWith("/infohash/")) { const infohash = url.pathname.split("/")[2]; - if (this.hashLocks.has(infohash)) { + if (this.processingRequests.has(infohash)) { console.log(` ${infohash} Waiting for existing request with same infohash`); - await this.hashLocks.get(infohash); + await this.processingRequests.get(infohash); } const processingPromise = (async () => { const file = await File.init({ infohash }, this._client, true); @@ -224,12 +224,12 @@ class RPCServer { return new Response(fileContent.file, { headers }); })(); - this.hashLocks.set(infohash, processingPromise); + this.processingRequests.set(infohash, processingPromise); try { await processingPromise; } finally { - this.hashLocks.delete(infohash); + this.processingRequests.delete(infohash); } } else if (url.pathname === "/upload") { const uploadSecret = req.headers.get("x-hydra-upload-secret"); @@ -299,9 +299,9 @@ class RPCServer { headers.set("hydra-signature", signature); return new Response(body, { headers }); } else { - if (this.hashLocks.has(hostname)) { + if (this.processingRequests.has(hostname)) { if (this._client.config.logLevel === "verbose") console.log(` ${hostname} Waiting for existing request with same hostname`); - await this.hashLocks.get(hostname); + await this.processingRequests.get(hostname); } if (hostname in this.cachedHostnames) return new Response(this.cachedHostnames[hostname].body, { headers: this.cachedHostnames[hostname].headers }); @@ -330,7 +330,7 @@ class RPCServer { })(); }); - this.hashLocks.set(hostname, processingPromise); + this.processingRequests.set(hostname, processingPromise); let response: Response | undefined; try { @@ -340,7 +340,7 @@ class RPCServer { if (err.message === "Hstname not found") return new Response("Hostname not found", { headers, status: 404 }); else throw err; } finally { - this.hashLocks.delete(hostname); + this.processingRequests.delete(hostname); } const res = { body: await response.text(), headers: response.headers }; this.cachedHostnames[hostname] = res; diff --git a/typedoc.jsonc b/typedoc.jsonc index 9f5b305..6131ce5 100644 --- a/typedoc.jsonc +++ b/typedoc.jsonc @@ -7,7 +7,7 @@ "projectDocuments": ["./docs"], "name": "Hydrafiles", "entryPointStrategy": "expand", - "exclude": ["./src/start.ts", "./src/filesystem/*FileSystem.ts"], + "exclude": ["./src/start.ts"], "customFooterHtml": "Free information for the people, by the people.
They can't stop us all.", "basePath": "./", "hideGenerator": true, @@ -20,7 +20,7 @@ "Web Dashboard": "/dashboard.html", "Source Code": "https://github.com/StarfilesFileSharing/Hydrafiles" }, - "excludeInternal": true, + "sourceLinkExternal": true, "sort": ["static-first", "required-first"] // "theme": "./typedoc-theme" }