@lordbex/thelounge 4.4.3-blowfish
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/.thelounge_home +1 -0
- package/LICENSE +22 -0
- package/README.md +95 -0
- package/client/index.html.tpl +69 -0
- package/dist/defaults/config.js +465 -0
- package/dist/package.json +174 -0
- package/dist/server/client.js +678 -0
- package/dist/server/clientManager.js +220 -0
- package/dist/server/command-line/index.js +85 -0
- package/dist/server/command-line/install.js +123 -0
- package/dist/server/command-line/outdated.js +30 -0
- package/dist/server/command-line/start.js +34 -0
- package/dist/server/command-line/storage.js +103 -0
- package/dist/server/command-line/uninstall.js +40 -0
- package/dist/server/command-line/upgrade.js +64 -0
- package/dist/server/command-line/users/add.js +67 -0
- package/dist/server/command-line/users/edit.js +39 -0
- package/dist/server/command-line/users/index.js +17 -0
- package/dist/server/command-line/users/list.js +53 -0
- package/dist/server/command-line/users/remove.js +37 -0
- package/dist/server/command-line/users/reset.js +64 -0
- package/dist/server/command-line/utils.js +177 -0
- package/dist/server/config.js +138 -0
- package/dist/server/helper.js +161 -0
- package/dist/server/identification.js +139 -0
- package/dist/server/index.js +3 -0
- package/dist/server/log.js +35 -0
- package/dist/server/models/chan.js +275 -0
- package/dist/server/models/msg.js +92 -0
- package/dist/server/models/network.js +546 -0
- package/dist/server/models/prefix.js +31 -0
- package/dist/server/models/user.js +42 -0
- package/dist/server/plugins/auth/ldap.js +188 -0
- package/dist/server/plugins/auth/local.js +41 -0
- package/dist/server/plugins/auth.js +70 -0
- package/dist/server/plugins/changelog.js +103 -0
- package/dist/server/plugins/clientCertificate.js +115 -0
- package/dist/server/plugins/dev-server.js +33 -0
- package/dist/server/plugins/inputs/action.js +54 -0
- package/dist/server/plugins/inputs/away.js +20 -0
- package/dist/server/plugins/inputs/ban.js +45 -0
- package/dist/server/plugins/inputs/blow.js +44 -0
- package/dist/server/plugins/inputs/connect.js +41 -0
- package/dist/server/plugins/inputs/ctcp.js +29 -0
- package/dist/server/plugins/inputs/disconnect.js +15 -0
- package/dist/server/plugins/inputs/ignore.js +74 -0
- package/dist/server/plugins/inputs/ignorelist.js +50 -0
- package/dist/server/plugins/inputs/index.js +105 -0
- package/dist/server/plugins/inputs/invite.js +31 -0
- package/dist/server/plugins/inputs/kick.js +26 -0
- package/dist/server/plugins/inputs/kill.js +13 -0
- package/dist/server/plugins/inputs/list.js +12 -0
- package/dist/server/plugins/inputs/mode.js +55 -0
- package/dist/server/plugins/inputs/msg.js +106 -0
- package/dist/server/plugins/inputs/mute.js +56 -0
- package/dist/server/plugins/inputs/nick.js +55 -0
- package/dist/server/plugins/inputs/notice.js +42 -0
- package/dist/server/plugins/inputs/part.js +46 -0
- package/dist/server/plugins/inputs/quit.js +27 -0
- package/dist/server/plugins/inputs/raw.js +13 -0
- package/dist/server/plugins/inputs/rejoin.js +25 -0
- package/dist/server/plugins/inputs/topic.js +24 -0
- package/dist/server/plugins/inputs/whois.js +19 -0
- package/dist/server/plugins/irc-events/away.js +59 -0
- package/dist/server/plugins/irc-events/cap.js +62 -0
- package/dist/server/plugins/irc-events/chghost.js +29 -0
- package/dist/server/plugins/irc-events/connection.js +152 -0
- package/dist/server/plugins/irc-events/ctcp.js +72 -0
- package/dist/server/plugins/irc-events/error.js +80 -0
- package/dist/server/plugins/irc-events/help.js +21 -0
- package/dist/server/plugins/irc-events/info.js +21 -0
- package/dist/server/plugins/irc-events/invite.js +27 -0
- package/dist/server/plugins/irc-events/join.js +53 -0
- package/dist/server/plugins/irc-events/kick.js +39 -0
- package/dist/server/plugins/irc-events/link.js +442 -0
- package/dist/server/plugins/irc-events/list.js +47 -0
- package/dist/server/plugins/irc-events/message.js +187 -0
- package/dist/server/plugins/irc-events/mode.js +124 -0
- package/dist/server/plugins/irc-events/modelist.js +67 -0
- package/dist/server/plugins/irc-events/motd.js +29 -0
- package/dist/server/plugins/irc-events/names.js +21 -0
- package/dist/server/plugins/irc-events/nick.js +45 -0
- package/dist/server/plugins/irc-events/part.js +35 -0
- package/dist/server/plugins/irc-events/quit.js +32 -0
- package/dist/server/plugins/irc-events/sasl.js +26 -0
- package/dist/server/plugins/irc-events/topic.js +42 -0
- package/dist/server/plugins/irc-events/unhandled.js +31 -0
- package/dist/server/plugins/irc-events/welcome.js +22 -0
- package/dist/server/plugins/irc-events/whois.js +57 -0
- package/dist/server/plugins/messageStorage/sqlite.js +454 -0
- package/dist/server/plugins/messageStorage/text.js +124 -0
- package/dist/server/plugins/packages/index.js +200 -0
- package/dist/server/plugins/packages/publicClient.js +66 -0
- package/dist/server/plugins/packages/themes.js +61 -0
- package/dist/server/plugins/storage.js +88 -0
- package/dist/server/plugins/sts.js +85 -0
- package/dist/server/plugins/uploader.js +267 -0
- package/dist/server/plugins/webpush.js +99 -0
- package/dist/server/server.js +857 -0
- package/dist/server/storageCleaner.js +131 -0
- package/dist/server/utils/fish.js +432 -0
- package/dist/shared/irc.js +19 -0
- package/dist/shared/linkify.js +81 -0
- package/dist/shared/types/chan.js +22 -0
- package/dist/shared/types/changelog.js +2 -0
- package/dist/shared/types/config.js +2 -0
- package/dist/shared/types/mention.js +2 -0
- package/dist/shared/types/msg.js +34 -0
- package/dist/shared/types/network.js +2 -0
- package/dist/shared/types/storage.js +2 -0
- package/dist/shared/types/user.js +2 -0
- package/dist/webpack.config.js +224 -0
- package/index.js +38 -0
- package/package.json +174 -0
- package/public/audio/pop.wav +0 -0
- package/public/css/style.css +12 -0
- package/public/css/style.css.map +1 -0
- package/public/favicon.ico +0 -0
- package/public/fonts/fa-solid-900.woff +0 -0
- package/public/fonts/fa-solid-900.woff2 +0 -0
- package/public/img/favicon-alerted.ico +0 -0
- package/public/img/icon-alerted-black-transparent-bg-72x72px.png +0 -0
- package/public/img/icon-alerted-grey-bg-192x192px.png +0 -0
- package/public/img/icon-black-transparent-bg.svg +1 -0
- package/public/img/logo-grey-bg-120x120px.png +0 -0
- package/public/img/logo-grey-bg-152x152px.png +0 -0
- package/public/img/logo-grey-bg-167x167px.png +0 -0
- package/public/img/logo-grey-bg-180x180px.png +0 -0
- package/public/img/logo-grey-bg-192x192px.png +0 -0
- package/public/img/logo-grey-bg-512x512px.png +0 -0
- package/public/img/logo-grey-bg.svg +1 -0
- package/public/img/logo-horizontal-transparent-bg-inverted.svg +1 -0
- package/public/img/logo-horizontal-transparent-bg.svg +1 -0
- package/public/img/logo-transparent-bg-inverted.svg +1 -0
- package/public/img/logo-transparent-bg.svg +1 -0
- package/public/img/logo-vertical-transparent-bg-inverted.svg +1 -0
- package/public/img/logo-vertical-transparent-bg.svg +1 -0
- package/public/js/bundle.js +2 -0
- package/public/js/bundle.js.map +1 -0
- package/public/js/bundle.vendor.js +3 -0
- package/public/js/bundle.vendor.js.LICENSE.txt +18 -0
- package/public/js/bundle.vendor.js.map +1 -0
- package/public/js/loading-error-handlers.js +1 -0
- package/public/robots.txt +2 -0
- package/public/service-worker.js +1 -0
- package/public/thelounge.webmanifest +53 -0
- package/public/themes/default.css +35 -0
- package/public/themes/morning.css +183 -0
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const lodash_1 = __importDefault(require("lodash"));
|
|
7
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
8
|
+
const crypto_1 = __importDefault(require("crypto"));
|
|
9
|
+
const fs_1 = __importDefault(require("fs"));
|
|
10
|
+
const path_1 = __importDefault(require("path"));
|
|
11
|
+
const auth_1 = __importDefault(require("./plugins/auth"));
|
|
12
|
+
const client_1 = __importDefault(require("./client"));
|
|
13
|
+
const config_1 = __importDefault(require("./config"));
|
|
14
|
+
const webpush_1 = __importDefault(require("./plugins/webpush"));
|
|
15
|
+
const log_1 = __importDefault(require("./log"));
|
|
16
|
+
class ClientManager {
|
|
17
|
+
clients;
|
|
18
|
+
sockets;
|
|
19
|
+
identHandler;
|
|
20
|
+
webPush;
|
|
21
|
+
constructor() {
|
|
22
|
+
this.clients = [];
|
|
23
|
+
}
|
|
24
|
+
init(identHandler, sockets) {
|
|
25
|
+
this.sockets = sockets;
|
|
26
|
+
this.identHandler = identHandler;
|
|
27
|
+
this.webPush = new webpush_1.default();
|
|
28
|
+
if (!config_1.default.values.public) {
|
|
29
|
+
this.loadUsers();
|
|
30
|
+
// LDAP does not have user commands, and users are dynamically
|
|
31
|
+
// created upon logon, so we don't need to watch for new files
|
|
32
|
+
if (!config_1.default.values.ldap.enable) {
|
|
33
|
+
this.autoloadUsers();
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
findClient(name) {
|
|
38
|
+
name = name.toLowerCase();
|
|
39
|
+
return this.clients.find((u) => u.name.toLowerCase() === name);
|
|
40
|
+
}
|
|
41
|
+
loadUsers() {
|
|
42
|
+
let users = this.getUsers();
|
|
43
|
+
if (users.length === 0) {
|
|
44
|
+
log_1.default.info(`There are currently no users. Create one with ${chalk_1.default.bold("thelounge add <name>")}.`);
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
const alreadySeenUsers = new Set();
|
|
48
|
+
users = users.filter((user) => {
|
|
49
|
+
user = user.toLowerCase();
|
|
50
|
+
if (alreadySeenUsers.has(user)) {
|
|
51
|
+
log_1.default.error(`There is more than one user named "${chalk_1.default.bold(user)}". Usernames are now case insensitive, duplicate users will not load.`);
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
alreadySeenUsers.add(user);
|
|
55
|
+
return true;
|
|
56
|
+
});
|
|
57
|
+
// This callback is used by Auth plugins to load users they deem acceptable
|
|
58
|
+
const callbackLoadUser = (user) => {
|
|
59
|
+
this.loadUser(user);
|
|
60
|
+
};
|
|
61
|
+
if (!auth_1.default.loadUsers(users, callbackLoadUser)) {
|
|
62
|
+
// Fallback to loading all users
|
|
63
|
+
users.forEach((name) => this.loadUser(name));
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
autoloadUsers() {
|
|
67
|
+
fs_1.default.watch(config_1.default.getUsersPath(), lodash_1.default.debounce(() => {
|
|
68
|
+
const loaded = this.clients.map((c) => c.name);
|
|
69
|
+
const updatedUsers = this.getUsers();
|
|
70
|
+
if (updatedUsers.length === 0) {
|
|
71
|
+
log_1.default.info(`There are currently no users. Create one with ${chalk_1.default.bold("thelounge add <name>")}.`);
|
|
72
|
+
}
|
|
73
|
+
// Reload all users. Existing users will only have their passwords reloaded.
|
|
74
|
+
updatedUsers.forEach((name) => this.loadUser(name));
|
|
75
|
+
// Existing users removed since last time users were loaded
|
|
76
|
+
lodash_1.default.difference(loaded, updatedUsers).forEach((name) => {
|
|
77
|
+
const client = lodash_1.default.find(this.clients, { name });
|
|
78
|
+
if (client) {
|
|
79
|
+
client.quit(true);
|
|
80
|
+
this.clients = lodash_1.default.without(this.clients, client);
|
|
81
|
+
log_1.default.info(`User ${chalk_1.default.bold(name)} disconnected and removed.`);
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
}, 1000, { maxWait: 10000 }));
|
|
85
|
+
}
|
|
86
|
+
loadUser(name) {
|
|
87
|
+
const userConfig = this.readUserConfig(name);
|
|
88
|
+
if (!userConfig) {
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
let client = this.findClient(name);
|
|
92
|
+
if (client) {
|
|
93
|
+
if (userConfig.password !== client.config.password) {
|
|
94
|
+
/**
|
|
95
|
+
* If we happen to reload an existing client, make super duper sure we
|
|
96
|
+
* have their latest password. We're not replacing the entire config
|
|
97
|
+
* object, because that could have undesired consequences.
|
|
98
|
+
*
|
|
99
|
+
* @see https://github.com/thelounge/thelounge/issues/598
|
|
100
|
+
*/
|
|
101
|
+
client.config.password = userConfig.password;
|
|
102
|
+
log_1.default.info(`Password for user ${chalk_1.default.bold(name)} was reset.`);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
client = new client_1.default(this, name, userConfig);
|
|
107
|
+
client.connect();
|
|
108
|
+
this.clients.push(client);
|
|
109
|
+
}
|
|
110
|
+
return client;
|
|
111
|
+
}
|
|
112
|
+
getUsers = function () {
|
|
113
|
+
if (!fs_1.default.existsSync(config_1.default.getUsersPath())) {
|
|
114
|
+
return [];
|
|
115
|
+
}
|
|
116
|
+
return fs_1.default
|
|
117
|
+
.readdirSync(config_1.default.getUsersPath())
|
|
118
|
+
.filter((file) => file.endsWith(".json"))
|
|
119
|
+
.map((file) => file.slice(0, -5));
|
|
120
|
+
};
|
|
121
|
+
addUser(name, password, enableLog) {
|
|
122
|
+
if (path_1.default.basename(name) !== name) {
|
|
123
|
+
throw new Error(`${name} is an invalid username.`);
|
|
124
|
+
}
|
|
125
|
+
const userPath = config_1.default.getUserConfigPath(name);
|
|
126
|
+
if (fs_1.default.existsSync(userPath)) {
|
|
127
|
+
log_1.default.error(`User ${chalk_1.default.green(name)} already exists.`);
|
|
128
|
+
return false;
|
|
129
|
+
}
|
|
130
|
+
const user = {
|
|
131
|
+
password: password || "",
|
|
132
|
+
log: enableLog,
|
|
133
|
+
};
|
|
134
|
+
try {
|
|
135
|
+
fs_1.default.writeFileSync(userPath, JSON.stringify(user, null, "\t"), {
|
|
136
|
+
mode: 0o600,
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
catch (e) {
|
|
140
|
+
log_1.default.error(`Failed to create user ${chalk_1.default.green(name)} (${e})`);
|
|
141
|
+
throw e;
|
|
142
|
+
}
|
|
143
|
+
try {
|
|
144
|
+
const userFolderStat = fs_1.default.statSync(config_1.default.getUsersPath());
|
|
145
|
+
const userFileStat = fs_1.default.statSync(userPath);
|
|
146
|
+
if (userFolderStat &&
|
|
147
|
+
userFileStat &&
|
|
148
|
+
(userFolderStat.uid !== userFileStat.uid || userFolderStat.gid !== userFileStat.gid)) {
|
|
149
|
+
log_1.default.warn(`User ${chalk_1.default.green(name)} has been created, but with a different uid (or gid) than expected.`);
|
|
150
|
+
log_1.default.warn("The file owner has been changed to the expected user. " +
|
|
151
|
+
"To prevent any issues, please run thelounge commands " +
|
|
152
|
+
"as the correct user that owns the config folder.");
|
|
153
|
+
log_1.default.warn("See https://thelounge.chat/docs/usage#using-the-correct-system-user for more information.");
|
|
154
|
+
fs_1.default.chownSync(userPath, userFolderStat.uid, userFolderStat.gid);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
catch (e) {
|
|
158
|
+
// We're simply verifying file owner as a safe guard for users
|
|
159
|
+
// that run `thelounge add` as root, so we don't care if it fails
|
|
160
|
+
}
|
|
161
|
+
return true;
|
|
162
|
+
}
|
|
163
|
+
getDataToSave(client) {
|
|
164
|
+
const json = Object.assign({}, client.config, {
|
|
165
|
+
networks: client.networks.map((n) => n.export()),
|
|
166
|
+
});
|
|
167
|
+
const newUser = JSON.stringify(json, null, "\t");
|
|
168
|
+
const newHash = crypto_1.default.createHash("sha256").update(newUser).digest("hex");
|
|
169
|
+
return { newUser, newHash };
|
|
170
|
+
}
|
|
171
|
+
saveUser(client, callback) {
|
|
172
|
+
const { newUser, newHash } = this.getDataToSave(client);
|
|
173
|
+
// Do not write to disk if the exported data hasn't actually changed
|
|
174
|
+
if (client.fileHash === newHash) {
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
const pathReal = config_1.default.getUserConfigPath(client.name);
|
|
178
|
+
const pathTemp = pathReal + ".tmp";
|
|
179
|
+
try {
|
|
180
|
+
// Write to a temp file first, in case the write fails
|
|
181
|
+
// we do not lose the original file (for example when disk is full)
|
|
182
|
+
fs_1.default.writeFileSync(pathTemp, newUser, {
|
|
183
|
+
mode: 0o600,
|
|
184
|
+
});
|
|
185
|
+
fs_1.default.renameSync(pathTemp, pathReal);
|
|
186
|
+
return callback ? callback() : true;
|
|
187
|
+
}
|
|
188
|
+
catch (e) {
|
|
189
|
+
log_1.default.error(`Failed to update user ${chalk_1.default.green(client.name)} (${e})`);
|
|
190
|
+
if (callback) {
|
|
191
|
+
callback(e);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
removeUser(name) {
|
|
196
|
+
const userPath = config_1.default.getUserConfigPath(name);
|
|
197
|
+
if (!fs_1.default.existsSync(userPath)) {
|
|
198
|
+
log_1.default.error(`Tried to remove non-existing user ${chalk_1.default.green(name)}.`);
|
|
199
|
+
return false;
|
|
200
|
+
}
|
|
201
|
+
fs_1.default.unlinkSync(userPath);
|
|
202
|
+
return true;
|
|
203
|
+
}
|
|
204
|
+
readUserConfig(name) {
|
|
205
|
+
const userPath = config_1.default.getUserConfigPath(name);
|
|
206
|
+
if (!fs_1.default.existsSync(userPath)) {
|
|
207
|
+
log_1.default.error(`Tried to read non-existing user ${chalk_1.default.green(name)}`);
|
|
208
|
+
return false;
|
|
209
|
+
}
|
|
210
|
+
try {
|
|
211
|
+
const data = fs_1.default.readFileSync(userPath, "utf-8");
|
|
212
|
+
return JSON.parse(data);
|
|
213
|
+
}
|
|
214
|
+
catch (e) {
|
|
215
|
+
log_1.default.error(`Failed to read user ${chalk_1.default.bold(name)}: ${e}`);
|
|
216
|
+
}
|
|
217
|
+
return false;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
exports.default = ClientManager;
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
/* eslint-disable @typescript-eslint/no-var-requires */
|
|
7
|
+
const log_1 = __importDefault(require("../log"));
|
|
8
|
+
const fs_1 = __importDefault(require("fs"));
|
|
9
|
+
const path_1 = __importDefault(require("path"));
|
|
10
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
11
|
+
const commander_1 = require("commander");
|
|
12
|
+
const helper_1 = __importDefault(require("../helper"));
|
|
13
|
+
const config_1 = __importDefault(require("../config"));
|
|
14
|
+
const utils_1 = __importDefault(require("./utils"));
|
|
15
|
+
const program = new commander_1.Command("thelounge");
|
|
16
|
+
program
|
|
17
|
+
.version(helper_1.default.getVersion(), "-v, --version")
|
|
18
|
+
.option("-c, --config <key=value>", "override entries of the configuration file, must be specified for each entry that needs to be overriden", utils_1.default.parseConfigOptions)
|
|
19
|
+
.on("--help", utils_1.default.extraHelp);
|
|
20
|
+
// Parse options from `argv` returning `argv` void of these options.
|
|
21
|
+
const argvWithoutOptions = program.parseOptions(process.argv);
|
|
22
|
+
config_1.default.setHome(process.env.THELOUNGE_HOME || utils_1.default.defaultHome());
|
|
23
|
+
// Check config file owner and warn if we're running under a different user
|
|
24
|
+
try {
|
|
25
|
+
verifyFileOwner();
|
|
26
|
+
}
|
|
27
|
+
catch (e) {
|
|
28
|
+
// We do not care about failures of these checks
|
|
29
|
+
// fs.statSync will throw if config.js does not exist (e.g. first run)
|
|
30
|
+
}
|
|
31
|
+
// Create packages/package.json
|
|
32
|
+
createPackagesFolder();
|
|
33
|
+
// Merge config key-values passed as CLI options into the main config
|
|
34
|
+
config_1.default.merge(program.opts().config);
|
|
35
|
+
program.addCommand(require("./start").default);
|
|
36
|
+
program.addCommand(require("./install").default);
|
|
37
|
+
program.addCommand(require("./uninstall").default);
|
|
38
|
+
program.addCommand(require("./upgrade").default);
|
|
39
|
+
program.addCommand(require("./outdated").default);
|
|
40
|
+
program.addCommand(require("./storage").default);
|
|
41
|
+
if (!config_1.default.values.public) {
|
|
42
|
+
require("./users").default.forEach((command) => {
|
|
43
|
+
if (command) {
|
|
44
|
+
program.addCommand(command);
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
// `parse` expects to be passed `process.argv`, but we need to remove to give it
|
|
49
|
+
// a version of `argv` that does not contain options already parsed by
|
|
50
|
+
// `parseOptions` above.
|
|
51
|
+
// This is done by giving it the updated `argv` that `parseOptions` returned,
|
|
52
|
+
// except it returns an object with `operands`/`unknown`, so we need to concat them.
|
|
53
|
+
// See https://github.com/tj/commander.js/blob/fefda77f463292/index.js#L686-L763
|
|
54
|
+
program.parse(argvWithoutOptions.operands.concat(argvWithoutOptions.unknown));
|
|
55
|
+
function createPackagesFolder() {
|
|
56
|
+
const packagesPath = config_1.default.getPackagesPath();
|
|
57
|
+
const packagesConfig = path_1.default.join(packagesPath, "package.json");
|
|
58
|
+
// Create node_modules folder, otherwise yarn will start walking upwards to find one
|
|
59
|
+
fs_1.default.mkdirSync(path_1.default.join(packagesPath, "node_modules"), { recursive: true });
|
|
60
|
+
// Create package.json with private set to true, if it doesn't exist already
|
|
61
|
+
if (!fs_1.default.existsSync(packagesConfig)) {
|
|
62
|
+
fs_1.default.writeFileSync(packagesConfig, JSON.stringify({
|
|
63
|
+
private: true,
|
|
64
|
+
description: "Packages for The Lounge. Use `thelounge install <package>` command to add a package.",
|
|
65
|
+
dependencies: {},
|
|
66
|
+
}, null, "\t"));
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
function verifyFileOwner() {
|
|
70
|
+
if (!process.getuid) {
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
const uid = process.getuid();
|
|
74
|
+
if (uid === 0) {
|
|
75
|
+
log_1.default.warn(`You are currently running The Lounge as root. ${chalk_1.default.bold.red("We highly discourage running as root!")}`);
|
|
76
|
+
}
|
|
77
|
+
const configStat = fs_1.default.statSync(path_1.default.join(config_1.default.getHomePath(), "config.js"));
|
|
78
|
+
if (configStat && configStat.uid !== uid) {
|
|
79
|
+
log_1.default.warn("Config file owner does not match the user you are currently running The Lounge as.");
|
|
80
|
+
log_1.default.warn("To prevent any issues, please run thelounge commands " +
|
|
81
|
+
"as the correct user that owns the config folder.");
|
|
82
|
+
log_1.default.warn("See https://thelounge.chat/docs/usage#using-the-correct-system-user for more information.");
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
exports.default = program;
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
26
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
27
|
+
};
|
|
28
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
|
+
/* eslint-disable @typescript-eslint/restrict-template-expressions */
|
|
30
|
+
const log_1 = __importDefault(require("../log"));
|
|
31
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
32
|
+
const semver_1 = __importDefault(require("semver"));
|
|
33
|
+
const helper_1 = __importDefault(require("../helper"));
|
|
34
|
+
const config_1 = __importDefault(require("../config"));
|
|
35
|
+
const utils_1 = __importDefault(require("./utils"));
|
|
36
|
+
const commander_1 = require("commander");
|
|
37
|
+
const program = new commander_1.Command("install");
|
|
38
|
+
program
|
|
39
|
+
.argument("<package>", "package to install. Use `file:$path_to_package_dir` to install a local package")
|
|
40
|
+
.description("Install a theme or a package")
|
|
41
|
+
.on("--help", utils_1.default.extraHelp)
|
|
42
|
+
.action(async function (packageName) {
|
|
43
|
+
const fs = await Promise.resolve().then(() => __importStar(require("fs")));
|
|
44
|
+
const fspromises = fs.promises;
|
|
45
|
+
const path = await Promise.resolve().then(() => __importStar(require("path")));
|
|
46
|
+
const packageJson = await Promise.resolve().then(() => __importStar(require("package-json")));
|
|
47
|
+
if (!fs.existsSync(config_1.default.getConfigPath())) {
|
|
48
|
+
log_1.default.error(`${config_1.default.getConfigPath()} does not exist.`);
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
log_1.default.info("Retrieving information about the package...");
|
|
52
|
+
// TODO: type
|
|
53
|
+
let readFile = null;
|
|
54
|
+
let isLocalFile = false;
|
|
55
|
+
if (packageName.startsWith("file:")) {
|
|
56
|
+
isLocalFile = true;
|
|
57
|
+
// our yarn invocation sets $HOME to the cachedir, so we must expand ~ now
|
|
58
|
+
// else the path will be invalid when npm expands it.
|
|
59
|
+
packageName = expandTildeInLocalPath(packageName);
|
|
60
|
+
readFile = fspromises
|
|
61
|
+
.readFile(path.join(packageName.substring("file:".length), "package.json"), "utf-8")
|
|
62
|
+
.then((data) => JSON.parse(data));
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
// properly split scoped and non-scoped npm packages
|
|
66
|
+
// into their name and version
|
|
67
|
+
let packageVersion = "latest";
|
|
68
|
+
const atIndex = packageName.indexOf("@", 1);
|
|
69
|
+
if (atIndex !== -1) {
|
|
70
|
+
packageVersion = packageName.slice(atIndex + 1);
|
|
71
|
+
packageName = packageName.slice(0, atIndex);
|
|
72
|
+
}
|
|
73
|
+
readFile = packageJson.default(packageName, {
|
|
74
|
+
fullMetadata: true,
|
|
75
|
+
version: packageVersion,
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
if (!readFile) {
|
|
79
|
+
// no-op, error should've been thrown before this point
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
readFile
|
|
83
|
+
.then((json) => {
|
|
84
|
+
const humanVersion = isLocalFile ? packageName : `${json.name} v${json.version}`;
|
|
85
|
+
if (!("thelounge" in json)) {
|
|
86
|
+
log_1.default.error(`${chalk_1.default.red(humanVersion)} does not have The Lounge metadata.`);
|
|
87
|
+
process.exit(1);
|
|
88
|
+
}
|
|
89
|
+
if (json.thelounge.supports &&
|
|
90
|
+
!semver_1.default.satisfies(helper_1.default.getVersionNumber(), json.thelounge.supports, {
|
|
91
|
+
includePrerelease: true,
|
|
92
|
+
})) {
|
|
93
|
+
log_1.default.error(`${chalk_1.default.red(humanVersion)} does not support The Lounge v${helper_1.default.getVersionNumber()}. Supported version(s): ${json.thelounge.supports}`);
|
|
94
|
+
process.exit(2);
|
|
95
|
+
}
|
|
96
|
+
log_1.default.info(`Installing ${chalk_1.default.green(humanVersion)}...`);
|
|
97
|
+
const yarnVersion = isLocalFile ? packageName : `${json.name}@${json.version}`;
|
|
98
|
+
return utils_1.default.executeYarnCommand("add", "--exact", yarnVersion)
|
|
99
|
+
.then(() => {
|
|
100
|
+
log_1.default.info(`${chalk_1.default.green(humanVersion)} has been successfully installed.`);
|
|
101
|
+
if (isLocalFile) {
|
|
102
|
+
// yarn v1 is buggy if a local filepath is used and doesn't update
|
|
103
|
+
// the lockfile properly. We need to run an install in that case
|
|
104
|
+
// even though that's supposed to be done by the add subcommand
|
|
105
|
+
return utils_1.default.executeYarnCommand("install").catch((err) => {
|
|
106
|
+
throw `Failed to update lockfile after package install ${err}`;
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
})
|
|
110
|
+
.catch((code) => {
|
|
111
|
+
throw `Failed to install ${chalk_1.default.red(humanVersion)}. Exit code: ${code}`;
|
|
112
|
+
});
|
|
113
|
+
})
|
|
114
|
+
.catch((e) => {
|
|
115
|
+
log_1.default.error(`${e}`);
|
|
116
|
+
process.exit(1);
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
function expandTildeInLocalPath(packageName) {
|
|
120
|
+
const path = packageName.substring("file:".length);
|
|
121
|
+
return "file:" + helper_1.default.expandHome(path);
|
|
122
|
+
}
|
|
123
|
+
exports.default = program;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const commander_1 = require("commander");
|
|
7
|
+
const utils_1 = __importDefault(require("./utils"));
|
|
8
|
+
const packages_1 = __importDefault(require("../plugins/packages"));
|
|
9
|
+
const log_1 = __importDefault(require("../log"));
|
|
10
|
+
const program = new commander_1.Command("outdated");
|
|
11
|
+
program
|
|
12
|
+
.description("Check for any outdated packages")
|
|
13
|
+
.on("--help", utils_1.default.extraHelp)
|
|
14
|
+
.action(async () => {
|
|
15
|
+
log_1.default.info("Checking for outdated packages");
|
|
16
|
+
await packages_1.default
|
|
17
|
+
.outdated(0)
|
|
18
|
+
.then((outdated) => {
|
|
19
|
+
if (outdated) {
|
|
20
|
+
log_1.default.info("There are outdated packages");
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
log_1.default.info("No outdated packages");
|
|
24
|
+
}
|
|
25
|
+
})
|
|
26
|
+
.catch(() => {
|
|
27
|
+
log_1.default.error("Error finding outdated packages.");
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
exports.default = program;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const log_1 = __importDefault(require("../log"));
|
|
7
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
8
|
+
const fs_1 = __importDefault(require("fs"));
|
|
9
|
+
const path_1 = __importDefault(require("path"));
|
|
10
|
+
const commander_1 = require("commander");
|
|
11
|
+
const config_1 = __importDefault(require("../config"));
|
|
12
|
+
const utils_1 = __importDefault(require("./utils"));
|
|
13
|
+
const program = new commander_1.Command("start");
|
|
14
|
+
program
|
|
15
|
+
.description("Start the server")
|
|
16
|
+
.option("--dev", "Development mode with hot module reloading")
|
|
17
|
+
.on("--help", utils_1.default.extraHelp)
|
|
18
|
+
.action(function (options) {
|
|
19
|
+
initalizeConfig();
|
|
20
|
+
const newLocal = "../server";
|
|
21
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
22
|
+
const server = require(newLocal);
|
|
23
|
+
server.default(options);
|
|
24
|
+
});
|
|
25
|
+
function initalizeConfig() {
|
|
26
|
+
if (!fs_1.default.existsSync(config_1.default.getConfigPath())) {
|
|
27
|
+
fs_1.default.mkdirSync(config_1.default.getHomePath(), { recursive: true });
|
|
28
|
+
fs_1.default.chmodSync(config_1.default.getHomePath(), "0700");
|
|
29
|
+
fs_1.default.copyFileSync(path_1.default.resolve(path_1.default.join(__dirname, "..", "..", "defaults", "config.js")), config_1.default.getConfigPath());
|
|
30
|
+
log_1.default.info(`Configuration file created at ${chalk_1.default.green(config_1.default.getConfigPath())}.`);
|
|
31
|
+
}
|
|
32
|
+
fs_1.default.mkdirSync(config_1.default.getUsersPath(), { recursive: true, mode: 0o700 });
|
|
33
|
+
}
|
|
34
|
+
exports.default = program;
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const log_1 = __importDefault(require("../log"));
|
|
7
|
+
const commander_1 = require("commander");
|
|
8
|
+
const clientManager_1 = __importDefault(require("../clientManager"));
|
|
9
|
+
const utils_1 = __importDefault(require("./utils"));
|
|
10
|
+
const sqlite_1 = __importDefault(require("../plugins/messageStorage/sqlite"));
|
|
11
|
+
const storageCleaner_1 = require("../storageCleaner");
|
|
12
|
+
const program = new commander_1.Command("storage").description("various utilities related to the message storage");
|
|
13
|
+
program
|
|
14
|
+
.command("migrate")
|
|
15
|
+
.argument("[username]", "migrate a specific user only, all if not provided")
|
|
16
|
+
.description("Migrate message storage where needed")
|
|
17
|
+
.on("--help", utils_1.default.extraHelp)
|
|
18
|
+
.action(function (user) {
|
|
19
|
+
runMigrations(user).catch((err) => {
|
|
20
|
+
log_1.default.error(err.toString());
|
|
21
|
+
process.exit(1);
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
program
|
|
25
|
+
.command("clean")
|
|
26
|
+
.argument("[user]", "clean messages for a specific user only, all if not provided")
|
|
27
|
+
.description("Delete messages from the DB based on the storage policy")
|
|
28
|
+
.on("--help", utils_1.default.extraHelp)
|
|
29
|
+
.action(function (user) {
|
|
30
|
+
runCleaning(user).catch((err) => {
|
|
31
|
+
log_1.default.error(err.toString());
|
|
32
|
+
process.exit(1);
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
async function runMigrations(user) {
|
|
36
|
+
const manager = new clientManager_1.default();
|
|
37
|
+
const users = manager.getUsers();
|
|
38
|
+
if (user) {
|
|
39
|
+
if (!users.includes(user)) {
|
|
40
|
+
throw new Error(`invalid user ${user}`);
|
|
41
|
+
}
|
|
42
|
+
return migrateUser(manager, user);
|
|
43
|
+
}
|
|
44
|
+
for (const name of users) {
|
|
45
|
+
await migrateUser(manager, name);
|
|
46
|
+
// if any migration fails we blow up,
|
|
47
|
+
// chances are the rest won't complete either
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
// runs sqlite migrations for a user, which must exist
|
|
51
|
+
async function migrateUser(manager, user) {
|
|
52
|
+
log_1.default.info("handling user", user);
|
|
53
|
+
if (!isUserLogEnabled(manager, user)) {
|
|
54
|
+
log_1.default.info("logging disabled for user", user, ". Skipping");
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
const sqlite = new sqlite_1.default(user);
|
|
58
|
+
await sqlite.enable(); // enable runs migrations
|
|
59
|
+
await sqlite.close();
|
|
60
|
+
log_1.default.info("user", user, "migrated successfully");
|
|
61
|
+
}
|
|
62
|
+
function isUserLogEnabled(manager, user) {
|
|
63
|
+
const conf = manager.readUserConfig(user);
|
|
64
|
+
if (!conf) {
|
|
65
|
+
log_1.default.error("Could not open user configuration of", user);
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
return conf.log;
|
|
69
|
+
}
|
|
70
|
+
async function runCleaning(user) {
|
|
71
|
+
const manager = new clientManager_1.default();
|
|
72
|
+
const users = manager.getUsers();
|
|
73
|
+
if (user) {
|
|
74
|
+
if (!users.includes(user)) {
|
|
75
|
+
throw new Error(`invalid user ${user}`);
|
|
76
|
+
}
|
|
77
|
+
return cleanUser(manager, user);
|
|
78
|
+
}
|
|
79
|
+
for (const name of users) {
|
|
80
|
+
await cleanUser(manager, name);
|
|
81
|
+
// if any migration fails we blow up,
|
|
82
|
+
// chances are the rest won't complete either
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
async function cleanUser(manager, user) {
|
|
86
|
+
log_1.default.info("handling user", user);
|
|
87
|
+
if (!isUserLogEnabled(manager, user)) {
|
|
88
|
+
log_1.default.info("logging disabled for user", user, ". Skipping");
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
const sqlite = new sqlite_1.default(user);
|
|
92
|
+
await sqlite.enable();
|
|
93
|
+
const cleaner = new storageCleaner_1.StorageCleaner(sqlite);
|
|
94
|
+
const num_deleted = await cleaner.runDeletesNoLimit();
|
|
95
|
+
log_1.default.info(`deleted ${num_deleted} messages`);
|
|
96
|
+
log_1.default.info("running a vacuum now, this might take a while");
|
|
97
|
+
if (num_deleted > 0) {
|
|
98
|
+
await sqlite.vacuum();
|
|
99
|
+
}
|
|
100
|
+
await sqlite.close();
|
|
101
|
+
log_1.default.info(`cleaning messages for ${user} has been successful`);
|
|
102
|
+
}
|
|
103
|
+
exports.default = program;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const log_1 = __importDefault(require("../log"));
|
|
7
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
8
|
+
const commander_1 = require("commander");
|
|
9
|
+
const config_1 = __importDefault(require("../config"));
|
|
10
|
+
const utils_1 = __importDefault(require("./utils"));
|
|
11
|
+
const program = new commander_1.Command("uninstall");
|
|
12
|
+
program
|
|
13
|
+
.argument("<package>", "The package to uninstall")
|
|
14
|
+
.description("Uninstall a theme or a package")
|
|
15
|
+
.on("--help", utils_1.default.extraHelp)
|
|
16
|
+
.action(async function (packageName) {
|
|
17
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
18
|
+
const fs = require("fs").promises;
|
|
19
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
20
|
+
const path = require("path");
|
|
21
|
+
const packagesConfig = path.join(config_1.default.getPackagesPath(), "package.json");
|
|
22
|
+
// const packages = JSON.parse(fs.readFileSync(packagesConfig, "utf-8"));
|
|
23
|
+
const packages = JSON.parse(await fs.readFile(packagesConfig, "utf-8"));
|
|
24
|
+
if (!packages.dependencies ||
|
|
25
|
+
!Object.prototype.hasOwnProperty.call(packages.dependencies, packageName)) {
|
|
26
|
+
log_1.default.warn(`${chalk_1.default.green(packageName)} is not installed.`);
|
|
27
|
+
process.exit(1);
|
|
28
|
+
}
|
|
29
|
+
log_1.default.info(`Uninstalling ${chalk_1.default.green(packageName)}...`);
|
|
30
|
+
try {
|
|
31
|
+
await utils_1.default.executeYarnCommand("remove", packageName);
|
|
32
|
+
log_1.default.info(`${chalk_1.default.green(packageName)} has been successfully uninstalled.`);
|
|
33
|
+
}
|
|
34
|
+
catch (code_1) {
|
|
35
|
+
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
|
|
36
|
+
log_1.default.error(`Failed to uninstall ${chalk_1.default.green(packageName)}. Exit code: ${code_1}`);
|
|
37
|
+
process.exit(1);
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
exports.default = program;
|