diff --git a/server/client.ts b/server/client.ts index 66a408c058..d5ffe84e16 100644 --- a/server/client.ts +++ b/server/client.ts @@ -15,7 +15,7 @@ import inputs from "./plugins/inputs"; import PublicClient from "./plugins/packages/publicClient"; import SqliteMessageStorage from "./plugins/messageStorage/sqlite"; import TextFileMessageStorage from "./plugins/messageStorage/text"; -import Network, {IgnoreListItem, NetworkWithIrcFramework} from "./models/network"; +import Network, {IgnoreListItem, NetworkConfig, NetworkWithIrcFramework} from "./models/network"; import ClientManager from "./clientManager"; import {MessageStorage, SearchQuery, SearchResponse} from "./plugins/messageStorage/types"; @@ -78,6 +78,7 @@ export type UserConfig = { hostname?: string; isSecure?: boolean; }; + networks?: NetworkConfig[]; }; export type Mention = { @@ -95,9 +96,7 @@ class Client { attachedClients!: { [socketId: string]: {token: string; openChannel: number}; }; - config!: UserConfig & { - networks?: Network[]; - }; + config!: UserConfig; id!: number; idMsg!: number; idChan!: number; @@ -176,8 +175,16 @@ class Client { this.registerPushSubscription(session, session.pushSubscription, true); } }); + } - (client.config.networks || []).forEach((network) => client.connect(network, true)); + connect() { + const client = this; + + if (client.networks.length !== 0) { + throw new Error(`${client.name} is already connected`); + } + + (client.config.networks || []).forEach((network) => client.connectToNetwork(network, true)); // Networks are stored directly in the client object // We don't need to keep it in the config object @@ -188,7 +195,7 @@ class Client { // Networks are created instantly, but to reduce server load on startup // We randomize the IRC connections and channel log loading - let delay = manager.clients.length * 500; + let delay = client.manager.clients.length * 500; client.networks.forEach((network) => { setTimeout(() => { network.channels.forEach((channel) => channel.loadMessages(client, network)); @@ -201,7 +208,7 @@ class Client { delay += 1000 + Math.floor(Math.random() * 1000); }); - client.fileHash = manager.getDataToSave(client).newHash; + client.fileHash = client.manager.getDataToSave(client).newHash; } } @@ -238,12 +245,10 @@ class Client { return false; } - connect(args: Record, isStartup = false) { + networkFromConfig(args: Record): Network { const client = this; - let channels: Chan[] = []; - // Get channel id for lobby before creating other channels for nicer ids - const lobbyChannelId = client.idChan++; + let channels: Chan[] = []; if (Array.isArray(args.channels)) { let badName = false; @@ -291,7 +296,7 @@ class Client { } // TODO; better typing for args - const network = new Network({ + return new Network({ uuid: args.uuid, name: String( args.name || (Config.values.lockNetwork ? Config.values.defaults.name : "") || "" @@ -319,6 +324,15 @@ class Client { proxyUsername: String(args.proxyUsername || ""), proxyPassword: String(args.proxyPassword || ""), }); + } + + connectToNetwork(args: Record, isStartup = false) { + const client = this; + + // Get channel id for lobby before creating other channels for nicer ids + const lobbyChannelId = client.idChan++; + + const network = this.networkFromConfig(args); // Set network lobby channel id network.getLobby().id = lobbyChannelId; @@ -359,7 +373,7 @@ class Client { if (!isStartup) { client.save(); - channels.forEach((channel) => channel.loadMessages(client, network)); + network.channels.forEach((channel) => channel.loadMessages(client, network)); } } diff --git a/server/clientManager.ts b/server/clientManager.ts index 705fa43212..78e94d1860 100644 --- a/server/clientManager.ts +++ b/server/clientManager.ts @@ -7,6 +7,7 @@ import path from "path"; import Auth from "./plugins/auth"; import Client, {UserConfig} from "./client"; import Config from "./config"; +import {NetworkConfig} from "./models/network"; import WebPush from "./plugins/webpush"; import log from "./log"; import {Server} from "socket.io"; @@ -144,6 +145,7 @@ class ClientManager { } } else { client = new Client(this, name, userConfig); + client.connect(); this.clients.push(client); } diff --git a/server/models/chan.ts b/server/models/chan.ts index 65a4067adb..82be21522d 100644 --- a/server/models/chan.ts +++ b/server/models/chan.ts @@ -33,6 +33,13 @@ export type FilteredChannel = Chan & { totalMessages: number; }; +export type ChanConfig = { + name: string; + key?: string; + muted?: boolean; + type?: string; +}; + class Chan { id: number; messages: Msg[]; diff --git a/server/models/network.ts b/server/models/network.ts index a47757f59d..269a415ed5 100644 --- a/server/models/network.ts +++ b/server/models/network.ts @@ -1,7 +1,7 @@ import _ from "lodash"; import {v4 as uuidv4} from "uuid"; import IrcFramework, {Client as IRCClient} from "irc-framework"; -import Chan, {Channel, ChanType} from "./chan"; +import Chan, {ChanConfig, Channel, ChanType} from "./chan"; import Msg, {MessageType} from "./msg"; import Prefix from "./prefix"; import Helper, {Hostmask} from "../helper"; @@ -67,6 +67,34 @@ export type NetworkWithIrcFramework = Network & { }; }; +export type NetworkConfig = { + nick: string; + name: string; + host: string; + port: number; + tls: boolean; + userDisconnected: boolean; + rejectUnauthorized: boolean; + password: string; + awayMessage: string; + commands: any[]; + username: string; + realname: string; + leaveMessage: string; + sasl: string; + saslAccount: string; + saslPassword: string; + channels: ChanConfig[]; + uuid: string; + proxyHost: string; + proxyPort: number; + proxyUsername: string; + proxyPassword: string; + proxyEnabled: boolean; + highlightRegex?: string; + ignoreList: any[]; +}; + class Network { nick: string; name: string; diff --git a/server/plugins/inputs/connect.ts b/server/plugins/inputs/connect.ts index 8ba60c20aa..85262e57c3 100644 --- a/server/plugins/inputs/connect.ts +++ b/server/plugins/inputs/connect.ts @@ -39,7 +39,7 @@ const input: PluginInputHandler = function (network, chan, cmd, args) { } const host = args[0]; - this.connect({host, port, tls}); + this.connectToNetwork({host, port, tls}); return true; }; diff --git a/server/server.ts b/server/server.ts index e5b005ddba..5ecdea9236 100644 --- a/server/server.ts +++ b/server/server.ts @@ -485,7 +485,7 @@ function initializeClient( data.commands = null; data.ignoreList = null; - client.connect(data); + client.connectToNetwork(data); } }); @@ -948,6 +948,7 @@ function performAuthentication(this: Socket, data) { if (Config.values.public) { client = new Client(manager!); + client.connect(); manager!.clients.push(client); socket.on("disconnect", function () { diff --git a/test/models/network.ts b/test/models/network.ts index 039866e85c..4cc63f00d9 100644 --- a/test/models/network.ts +++ b/test/models/network.ts @@ -30,7 +30,7 @@ describe("Network", function () { expect(network1.uuid).to.not.equal(network2.uuid); }); - it("lobby should be at the top", function () { + it("should keep the lobby at the top", function () { const network = new Network({ name: "Super Nice Network", channels: [ diff --git a/test/tests/customhighlights.ts b/test/tests/customhighlights.ts index d9a8cfd43a..30f84c0193 100644 --- a/test/tests/customhighlights.ts +++ b/test/tests/customhighlights.ts @@ -27,6 +27,7 @@ describe("Custom highlights", function () { }, } as any ); + client.connect(); logInfoStub.restore(); expect(userLoadedLog).to.equal("User test loaded\n");