@lordbex/thelounge 4.4.3-blowfish → 4.5.0-blowfish-pre
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/README.md +31 -7
- package/dist/defaults/config.js +31 -2
- package/dist/package.json +94 -91
- package/dist/server/client.js +188 -194
- package/dist/server/clientManager.js +75 -72
- package/dist/server/command-line/index.js +44 -43
- package/dist/server/command-line/install.js +37 -70
- package/dist/server/command-line/outdated.js +12 -17
- package/dist/server/command-line/start.js +25 -26
- package/dist/server/command-line/storage.js +26 -31
- package/dist/server/command-line/uninstall.js +16 -23
- package/dist/server/command-line/upgrade.js +20 -26
- package/dist/server/command-line/users/add.js +33 -40
- package/dist/server/command-line/users/edit.js +18 -24
- package/dist/server/command-line/users/index.js +12 -16
- package/dist/server/command-line/users/list.js +11 -39
- package/dist/server/command-line/users/remove.js +16 -22
- package/dist/server/command-line/users/reset.js +34 -35
- package/dist/server/command-line/utils.js +231 -87
- package/dist/server/config.js +61 -52
- package/dist/server/helper.js +29 -28
- package/dist/server/identification.js +39 -34
- package/dist/server/index.js +1 -3
- package/dist/server/log.js +19 -16
- package/dist/server/models/chan.js +36 -33
- package/dist/server/models/msg.js +15 -19
- package/dist/server/models/network.js +102 -104
- package/dist/server/models/prefix.js +4 -7
- package/dist/server/models/user.js +5 -10
- package/dist/server/path-helper.js +8 -0
- package/dist/server/plugins/auth/ldap.js +177 -112
- package/dist/server/plugins/auth/local.js +10 -15
- package/dist/server/plugins/auth.js +6 -35
- package/dist/server/plugins/changelog.js +30 -27
- package/dist/server/plugins/clientCertificate.js +33 -37
- package/dist/server/plugins/dev-server.js +15 -21
- package/dist/server/plugins/inputs/action.js +9 -14
- package/dist/server/plugins/inputs/away.js +1 -3
- package/dist/server/plugins/inputs/ban.js +9 -14
- package/dist/server/plugins/inputs/blow.js +9 -14
- package/dist/server/plugins/inputs/connect.js +5 -10
- package/dist/server/plugins/inputs/ctcp.js +7 -12
- package/dist/server/plugins/inputs/disconnect.js +1 -3
- package/dist/server/plugins/inputs/ignore.js +23 -29
- package/dist/server/plugins/inputs/ignorelist.js +12 -18
- package/dist/server/plugins/inputs/index.js +8 -34
- package/dist/server/plugins/inputs/invite.js +7 -12
- package/dist/server/plugins/inputs/kick.js +7 -12
- package/dist/server/plugins/inputs/kill.js +1 -3
- package/dist/server/plugins/inputs/list.js +1 -3
- package/dist/server/plugins/inputs/mode.js +10 -15
- package/dist/server/plugins/inputs/msg.js +13 -18
- package/dist/server/plugins/inputs/mute.js +9 -15
- package/dist/server/plugins/inputs/nick.js +9 -14
- package/dist/server/plugins/inputs/notice.js +5 -7
- package/dist/server/plugins/inputs/part.js +11 -16
- package/dist/server/plugins/inputs/quit.js +7 -13
- package/dist/server/plugins/inputs/rainbow.js +55 -0
- package/dist/server/plugins/inputs/raw.js +1 -3
- package/dist/server/plugins/inputs/rejoin.js +7 -12
- package/dist/server/plugins/inputs/topic.js +7 -12
- package/dist/server/plugins/inputs/whois.js +1 -3
- package/dist/server/plugins/irc-events/away.js +14 -20
- package/dist/server/plugins/irc-events/cap.js +16 -22
- package/dist/server/plugins/irc-events/chghost.js +14 -13
- package/dist/server/plugins/irc-events/connection.js +61 -63
- package/dist/server/plugins/irc-events/ctcp.js +22 -28
- package/dist/server/plugins/irc-events/error.js +20 -26
- package/dist/server/plugins/irc-events/help.js +7 -13
- package/dist/server/plugins/irc-events/info.js +7 -13
- package/dist/server/plugins/irc-events/invite.js +7 -13
- package/dist/server/plugins/irc-events/join.js +30 -27
- package/dist/server/plugins/irc-events/kick.js +21 -17
- package/dist/server/plugins/irc-events/link.js +122 -109
- package/dist/server/plugins/irc-events/list.js +23 -26
- package/dist/server/plugins/irc-events/message.js +46 -52
- package/dist/server/plugins/irc-events/mode.js +66 -63
- package/dist/server/plugins/irc-events/modelist.js +29 -35
- package/dist/server/plugins/irc-events/motd.js +10 -16
- package/dist/server/plugins/irc-events/names.js +3 -6
- package/dist/server/plugins/irc-events/nick.js +26 -23
- package/dist/server/plugins/irc-events/part.js +19 -15
- package/dist/server/plugins/irc-events/quit.js +17 -14
- package/dist/server/plugins/irc-events/sasl.js +9 -15
- package/dist/server/plugins/irc-events/spgroups.js +38 -0
- package/dist/server/plugins/irc-events/spjoin.js +52 -0
- package/dist/server/plugins/irc-events/topic.js +12 -18
- package/dist/server/plugins/irc-events/unhandled.js +12 -12
- package/dist/server/plugins/irc-events/welcome.js +7 -13
- package/dist/server/plugins/irc-events/whois.js +20 -24
- package/dist/server/plugins/massEventAggregator.js +214 -0
- package/dist/server/plugins/messageStorage/sqlite.js +322 -141
- package/dist/server/plugins/messageStorage/text.js +21 -26
- package/dist/server/plugins/packages/index.js +105 -74
- package/dist/server/plugins/packages/publicClient.js +7 -16
- package/dist/server/plugins/packages/themes.js +11 -16
- package/dist/server/plugins/storage.js +28 -33
- package/dist/server/plugins/sts.js +12 -17
- package/dist/server/plugins/uploader.js +40 -43
- package/dist/server/plugins/webpush.js +23 -51
- package/dist/server/server.js +318 -271
- package/dist/server/storageCleaner.js +29 -37
- package/dist/server/utils/fish.js +349 -389
- package/dist/shared/irc.js +3 -6
- package/dist/shared/linkify.js +7 -14
- package/dist/shared/types/chan.js +6 -9
- package/dist/shared/types/changelog.js +1 -2
- package/dist/shared/types/config.js +1 -2
- package/dist/shared/types/mention.js +1 -2
- package/dist/shared/types/msg.js +3 -5
- package/dist/shared/types/network.js +1 -2
- package/dist/shared/types/storage.js +1 -2
- package/dist/shared/types/user.js +1 -2
- package/index.js +14 -10
- package/package.json +94 -91
- package/public/css/style.css +9 -6
- package/public/css/style.css.map +1 -1
- package/public/fonts/font-awesome/fa-brands-400.ttf +0 -0
- package/public/fonts/font-awesome/fa-brands-400.woff2 +0 -0
- package/public/fonts/font-awesome/fa-duotone-900.ttf +0 -0
- package/public/fonts/font-awesome/fa-duotone-900.woff2 +0 -0
- package/public/fonts/font-awesome/fa-light-300.ttf +0 -0
- package/public/fonts/font-awesome/fa-light-300.woff2 +0 -0
- package/public/fonts/font-awesome/fa-regular-400.ttf +0 -0
- package/public/fonts/font-awesome/fa-regular-400.woff2 +0 -0
- package/public/fonts/font-awesome/fa-solid-900.ttf +0 -0
- package/public/fonts/font-awesome/fa-solid-900.woff2 +0 -0
- package/public/fonts/font-awesome/fa-thin-100.ttf +0 -0
- package/public/fonts/font-awesome/fa-thin-100.woff2 +0 -0
- package/public/fonts/font-awesome/fa-v4compatibility.ttf +0 -0
- package/public/fonts/font-awesome/fa-v4compatibility.woff2 +0 -0
- package/public/js/bundle.js +1 -1
- package/public/js/bundle.js.map +1 -1
- package/public/js/bundle.vendor.js +1 -1
- package/public/js/bundle.vendor.js.LICENSE.txt +24 -6
- package/public/js/bundle.vendor.js.map +1 -1
- package/public/js/loading-error-handlers.js +1 -1
- package/public/service-worker.js +1 -1
- package/public/themes/default.css +1 -1
- package/public/themes/morning.css +1 -1
- package/dist/webpack.config.js +0 -224
|
@@ -1,26 +1,21 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
const lodash_1 = __importDefault(require("lodash"));
|
|
7
|
-
const fs_1 = __importDefault(require("fs"));
|
|
8
|
-
const path_1 = __importDefault(require("path"));
|
|
9
|
-
const log_1 = __importDefault(require("../log"));
|
|
10
|
-
const config_1 = __importDefault(require("../config"));
|
|
1
|
+
import _ from "lodash";
|
|
2
|
+
import fs from "fs";
|
|
3
|
+
import path from "path";
|
|
4
|
+
import log from "../log.js";
|
|
5
|
+
import Config from "../config.js";
|
|
11
6
|
class STSPolicies {
|
|
12
7
|
stsFile;
|
|
13
8
|
refresh;
|
|
14
9
|
policies;
|
|
15
10
|
constructor() {
|
|
16
|
-
this.stsFile =
|
|
11
|
+
this.stsFile = path.join(Config.getHomePath(), "sts-policies.json");
|
|
17
12
|
this.policies = new Map();
|
|
18
13
|
// eslint-disable-next-line @typescript-eslint/unbound-method
|
|
19
|
-
this.refresh =
|
|
20
|
-
if (!
|
|
14
|
+
this.refresh = _.debounce(this.saveFile, 10000, { maxWait: 60000 });
|
|
15
|
+
if (!fs.existsSync(this.stsFile)) {
|
|
21
16
|
return;
|
|
22
17
|
}
|
|
23
|
-
const storedPolicies = JSON.parse(
|
|
18
|
+
const storedPolicies = JSON.parse(fs.readFileSync(this.stsFile, "utf-8"));
|
|
24
19
|
const now = Date.now();
|
|
25
20
|
storedPolicies.forEach((value) => {
|
|
26
21
|
if (value.expires > now) {
|
|
@@ -75,11 +70,11 @@ class STSPolicies {
|
|
|
75
70
|
});
|
|
76
71
|
});
|
|
77
72
|
const file = JSON.stringify(policiesToStore, null, "\t");
|
|
78
|
-
|
|
73
|
+
fs.writeFile(this.stsFile, file, { flag: "w+" }, (err) => {
|
|
79
74
|
if (err) {
|
|
80
|
-
|
|
75
|
+
log.error("Failed to update STS policies file!", err.message);
|
|
81
76
|
}
|
|
82
77
|
});
|
|
83
78
|
}
|
|
84
79
|
}
|
|
85
|
-
|
|
80
|
+
export default new STSPolicies();
|
|
@@ -1,19 +1,13 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
const file_type_1 = __importDefault(require("file-type"));
|
|
12
|
-
const read_chunk_1 = __importDefault(require("read-chunk"));
|
|
13
|
-
const crypto_1 = __importDefault(require("crypto"));
|
|
14
|
-
const is_utf8_1 = __importDefault(require("is-utf8"));
|
|
15
|
-
const log_1 = __importDefault(require("../log"));
|
|
16
|
-
const content_disposition_1 = __importDefault(require("content-disposition"));
|
|
1
|
+
import Config from "../config.js";
|
|
2
|
+
import busboy from "@fastify/busboy";
|
|
3
|
+
import { v4 as uuidv4 } from "uuid";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import fs from "node:fs";
|
|
6
|
+
import { fileTypeFromBuffer } from "file-type";
|
|
7
|
+
import { readChunk } from "read-chunk";
|
|
8
|
+
import crypto from "node:crypto";
|
|
9
|
+
import log from "../log.js";
|
|
10
|
+
import contentDisposition from "content-disposition";
|
|
17
11
|
// Map of allowed mime types to their respecive default filenames
|
|
18
12
|
// that will be rendered in browser without forcing them to be downloaded
|
|
19
13
|
const inlineContentDispositionTypes = {
|
|
@@ -40,7 +34,7 @@ const uploadTokens = new Map();
|
|
|
40
34
|
class Uploader {
|
|
41
35
|
constructor(socket) {
|
|
42
36
|
socket.on("upload:auth", () => {
|
|
43
|
-
const token = (
|
|
37
|
+
const token = uuidv4();
|
|
44
38
|
socket.emit("upload:auth", token);
|
|
45
39
|
// Invalidate the token in one minute
|
|
46
40
|
const timeout = Uploader.createTokenTimeout(token);
|
|
@@ -62,9 +56,8 @@ class Uploader {
|
|
|
62
56
|
static createTokenTimeout(token) {
|
|
63
57
|
return setTimeout(() => uploadTokens.delete(token), 60 * 1000);
|
|
64
58
|
}
|
|
65
|
-
// TODO: type
|
|
66
59
|
static router(express) {
|
|
67
|
-
express.get("/uploads/:name/:slug
|
|
60
|
+
express.get("/uploads/:name{/:slug}", Uploader.routeGetFile);
|
|
68
61
|
express.post("/uploads/new/:token", Uploader.routeUploadFile);
|
|
69
62
|
}
|
|
70
63
|
static async routeGetFile(req, res) {
|
|
@@ -74,8 +67,8 @@ class Uploader {
|
|
|
74
67
|
return res.status(404).send("Not found");
|
|
75
68
|
}
|
|
76
69
|
const folder = name.substring(0, 2);
|
|
77
|
-
const uploadPath =
|
|
78
|
-
const filePath =
|
|
70
|
+
const uploadPath = Config.getFileUploadPath();
|
|
71
|
+
const filePath = path.join(uploadPath, folder, name);
|
|
79
72
|
let detectedMimeType = await Uploader.getFileType(filePath);
|
|
80
73
|
// doesn't exist
|
|
81
74
|
if (detectedMimeType === null) {
|
|
@@ -89,7 +82,7 @@ class Uploader {
|
|
|
89
82
|
slug = inlineContentDispositionTypes[detectedMimeType];
|
|
90
83
|
}
|
|
91
84
|
if (slug) {
|
|
92
|
-
disposition = (
|
|
85
|
+
disposition = contentDisposition(slug.trim(), {
|
|
93
86
|
fallback: false,
|
|
94
87
|
type: disposition,
|
|
95
88
|
});
|
|
@@ -137,11 +130,11 @@ class Uploader {
|
|
|
137
130
|
const abortWithError = (err) => {
|
|
138
131
|
doneCallback();
|
|
139
132
|
// if we ended up erroring out, delete the output file from disk
|
|
140
|
-
if (destPath &&
|
|
141
|
-
|
|
133
|
+
if (destPath && fs.existsSync(destPath)) {
|
|
134
|
+
fs.unlinkSync(destPath);
|
|
142
135
|
destPath = null;
|
|
143
136
|
}
|
|
144
|
-
return res.status(400).json({ error: err.message });
|
|
137
|
+
return res.status(400).json({ error: err instanceof Error ? err.message : String(err) });
|
|
145
138
|
};
|
|
146
139
|
// if the authentication token is incorrect, bail out
|
|
147
140
|
if (uploadTokens.delete(req.params.token) !== true) {
|
|
@@ -159,7 +152,7 @@ class Uploader {
|
|
|
159
152
|
// create a new busboy processor, it is wrapped in try/catch
|
|
160
153
|
// because it can throw on malformed headers
|
|
161
154
|
try {
|
|
162
|
-
busboyInstance = new
|
|
155
|
+
busboyInstance = new busboy({
|
|
163
156
|
headers: req.headers,
|
|
164
157
|
limits: {
|
|
165
158
|
files: 1, // only allow one file per upload
|
|
@@ -179,34 +172,33 @@ class Uploader {
|
|
|
179
172
|
// we use do/while loop to prevent the rare case of generating a file name
|
|
180
173
|
// that already exists on disk
|
|
181
174
|
do {
|
|
182
|
-
randomName =
|
|
183
|
-
destDir =
|
|
184
|
-
destPath =
|
|
185
|
-
} while (
|
|
175
|
+
randomName = crypto.randomBytes(8).toString("hex");
|
|
176
|
+
destDir = path.join(Config.getFileUploadPath(), randomName.substring(0, 2));
|
|
177
|
+
destPath = path.join(destDir, randomName);
|
|
178
|
+
} while (fs.existsSync(destPath));
|
|
186
179
|
// we split the filename into subdirectories (by taking 2 letters from the beginning)
|
|
187
180
|
// this helps avoid file system and certain tooling limitations when there are
|
|
188
181
|
// too many files on one folder
|
|
189
182
|
try {
|
|
190
|
-
|
|
183
|
+
fs.mkdirSync(destDir, { recursive: true });
|
|
191
184
|
}
|
|
192
185
|
catch (err) {
|
|
193
|
-
|
|
186
|
+
log.error(`Error ensuring ${destDir} exists for uploads: ${err instanceof Error ? err.message : String(err)}`);
|
|
194
187
|
return abortWithError(err);
|
|
195
188
|
}
|
|
196
189
|
// Open a file stream for writing
|
|
197
|
-
streamWriter =
|
|
190
|
+
streamWriter = fs.createWriteStream(destPath);
|
|
198
191
|
streamWriter.on("error", abortWithError);
|
|
199
192
|
busboyInstance.on("file", (fieldname, fileStream, filename) => {
|
|
200
193
|
uploadUrl = `${randomName}/${encodeURIComponent(filename)}`;
|
|
201
|
-
if (
|
|
202
|
-
uploadUrl = new URL(uploadUrl,
|
|
194
|
+
if (Config.values.fileUpload.baseUrl) {
|
|
195
|
+
uploadUrl = new URL(uploadUrl, Config.values.fileUpload.baseUrl).toString();
|
|
203
196
|
}
|
|
204
197
|
else {
|
|
205
198
|
uploadUrl = `uploads/${uploadUrl}`;
|
|
206
199
|
}
|
|
207
200
|
// if the busboy data stream errors out or goes over the file size limit
|
|
208
201
|
// abort the processing with an error
|
|
209
|
-
// @ts-expect-error Argument of type '(err: any) => Response<any, Record<string, any>>' is not assignable to parameter of type '{ (err: any): Response<any, Record<string, any>>; (): void; }'.ts(2345)
|
|
210
202
|
fileStream.on("error", abortWithError);
|
|
211
203
|
fileStream.on("limit", () => {
|
|
212
204
|
fileStream.unpipe(streamWriter);
|
|
@@ -230,7 +222,7 @@ class Uploader {
|
|
|
230
222
|
return req.pipe(busboyInstance);
|
|
231
223
|
}
|
|
232
224
|
static getMaxFileSize() {
|
|
233
|
-
const configOption =
|
|
225
|
+
const configOption = Config.values.fileUpload.maxFileSize;
|
|
234
226
|
// Busboy uses Infinity to allow unlimited file size
|
|
235
227
|
if (configOption < 1) {
|
|
236
228
|
return Infinity;
|
|
@@ -242,26 +234,31 @@ class Uploader {
|
|
|
242
234
|
// Returns a string with the type otherwise
|
|
243
235
|
static async getFileType(filePath) {
|
|
244
236
|
try {
|
|
245
|
-
const buffer = await (
|
|
237
|
+
const buffer = await readChunk(filePath, { length: 5120, startPosition: 0 });
|
|
246
238
|
// returns {ext, mime} if found, null if not.
|
|
247
|
-
const file = await
|
|
239
|
+
const file = await fileTypeFromBuffer(buffer);
|
|
248
240
|
// if a file type was detected correctly, return it
|
|
249
241
|
if (file) {
|
|
250
242
|
return file.mime;
|
|
251
243
|
}
|
|
252
244
|
// if the buffer is a valid UTF-8 buffer, use text/plain
|
|
253
|
-
|
|
245
|
+
try {
|
|
246
|
+
new TextDecoder("utf-8", { fatal: true }).decode(buffer);
|
|
254
247
|
return "text/plain";
|
|
255
248
|
}
|
|
249
|
+
catch {
|
|
250
|
+
// Not valid UTF-8, continue to detect as binary
|
|
251
|
+
}
|
|
256
252
|
// otherwise assume it's random binary data
|
|
257
253
|
return "application/octet-stream";
|
|
258
254
|
}
|
|
259
255
|
catch (e) {
|
|
260
|
-
if (e.code !== "ENOENT") {
|
|
261
|
-
|
|
256
|
+
if (e && typeof e === "object" && "code" in e && e.code !== "ENOENT") {
|
|
257
|
+
const message = e instanceof Error ? e.message : "unknown error";
|
|
258
|
+
log.warn(`Failed to read ${filePath}: ${message}`);
|
|
262
259
|
}
|
|
263
260
|
}
|
|
264
261
|
return null;
|
|
265
262
|
}
|
|
266
263
|
}
|
|
267
|
-
|
|
264
|
+
export default Uploader;
|
|
@@ -1,45 +1,17 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
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
|
-
const lodash_1 = __importDefault(require("lodash"));
|
|
30
|
-
const log_1 = __importDefault(require("../log"));
|
|
31
|
-
const fs_1 = __importDefault(require("fs"));
|
|
32
|
-
const path_1 = __importDefault(require("path"));
|
|
33
|
-
const web_push_1 = __importDefault(require("web-push"));
|
|
34
|
-
const config_1 = __importDefault(require("../config"));
|
|
35
|
-
const os = __importStar(require("os"));
|
|
1
|
+
import _ from "lodash";
|
|
2
|
+
import log from "../log.js";
|
|
3
|
+
import fs from "fs";
|
|
4
|
+
import path from "path";
|
|
5
|
+
import WebPushAPI from "web-push";
|
|
6
|
+
import Config from "../config.js";
|
|
7
|
+
import * as os from "os";
|
|
36
8
|
class WebPush {
|
|
37
9
|
vapidKeys;
|
|
38
10
|
constructor() {
|
|
39
|
-
const vapidPath =
|
|
11
|
+
const vapidPath = path.join(Config.getHomePath(), "vapid.json");
|
|
40
12
|
let vapidStat = undefined;
|
|
41
13
|
try {
|
|
42
|
-
vapidStat =
|
|
14
|
+
vapidStat = fs.statSync(vapidPath);
|
|
43
15
|
}
|
|
44
16
|
catch {
|
|
45
17
|
// ignored on purpose, node v14.17.0 will give us {throwIfNoEntry: false}
|
|
@@ -47,12 +19,12 @@ class WebPush {
|
|
|
47
19
|
if (vapidStat) {
|
|
48
20
|
const isWorldReadable = (vapidStat.mode & 0o004) !== 0;
|
|
49
21
|
if (isWorldReadable) {
|
|
50
|
-
|
|
22
|
+
log.warn(vapidPath, "is world readable.", "The file contains secrets. Please fix the permissions.");
|
|
51
23
|
if (os.platform() !== "win32") {
|
|
52
|
-
|
|
24
|
+
log.warn(`run \`chmod o= "${vapidPath}"\` to correct it.`);
|
|
53
25
|
}
|
|
54
26
|
}
|
|
55
|
-
const data =
|
|
27
|
+
const data = fs.readFileSync(vapidPath, "utf-8");
|
|
56
28
|
const parsedData = JSON.parse(data);
|
|
57
29
|
if (typeof parsedData.publicKey === "string" &&
|
|
58
30
|
typeof parsedData.privateKey === "string") {
|
|
@@ -63,18 +35,18 @@ class WebPush {
|
|
|
63
35
|
}
|
|
64
36
|
}
|
|
65
37
|
if (!this.vapidKeys) {
|
|
66
|
-
this.vapidKeys =
|
|
67
|
-
|
|
38
|
+
this.vapidKeys = WebPushAPI.generateVAPIDKeys();
|
|
39
|
+
fs.writeFileSync(vapidPath, JSON.stringify(this.vapidKeys, null, "\t"), {
|
|
68
40
|
mode: 0o600,
|
|
69
41
|
});
|
|
70
|
-
|
|
42
|
+
log.info("New VAPID key pair has been generated for use with push subscription.");
|
|
71
43
|
}
|
|
72
|
-
|
|
44
|
+
WebPushAPI.setVapidDetails("https://github.com/lordbex/thelounge", this.vapidKeys.publicKey, this.vapidKeys.privateKey);
|
|
73
45
|
}
|
|
74
46
|
push(client, payload, onlyToOffline) {
|
|
75
|
-
|
|
47
|
+
_.forOwn(client.config.sessions, ({ pushSubscription }, token) => {
|
|
76
48
|
if (pushSubscription) {
|
|
77
|
-
if (onlyToOffline &&
|
|
49
|
+
if (onlyToOffline && _.find(client.attachedClients, { token }) !== undefined) {
|
|
78
50
|
return;
|
|
79
51
|
}
|
|
80
52
|
this.pushSingle(client, pushSubscription, payload);
|
|
@@ -82,18 +54,18 @@ class WebPush {
|
|
|
82
54
|
});
|
|
83
55
|
}
|
|
84
56
|
pushSingle(client, subscription, payload) {
|
|
85
|
-
|
|
57
|
+
WebPushAPI.sendNotification(subscription, JSON.stringify(payload)).catch((error) => {
|
|
86
58
|
if (error.statusCode >= 400 && error.statusCode < 500) {
|
|
87
|
-
|
|
88
|
-
|
|
59
|
+
log.warn(`WebPush subscription for ${client.name} returned an error (${String(error.statusCode)}), removing subscription`);
|
|
60
|
+
_.forOwn(client.config.sessions, ({ pushSubscription }, token) => {
|
|
89
61
|
if (pushSubscription && pushSubscription.endpoint === subscription.endpoint) {
|
|
90
62
|
client.unregisterPushSubscription(token);
|
|
91
63
|
}
|
|
92
64
|
});
|
|
93
65
|
return;
|
|
94
66
|
}
|
|
95
|
-
|
|
67
|
+
log.error(`WebPush Error (${String(error)})`);
|
|
96
68
|
});
|
|
97
69
|
}
|
|
98
70
|
}
|
|
99
|
-
|
|
71
|
+
export default WebPush;
|