Compare commits
No commits in common. "d66f8005de9ef59a6427864ca7940cc27e98408e" and "5eb40f9ba7bde977aebcad94ab4cd75b23c7d47a" have entirely different histories.
d66f8005de
...
5eb40f9ba7
16
package.json
16
package.json
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@supernets/hardlounge",
|
"name": "@supernets/hardlounge",
|
||||||
"description": "The self-hosted Web IRC client",
|
"description": "The self-hosted Web IRC client",
|
||||||
"version": "4.4.1-1",
|
"version": "4.4.1",
|
||||||
"preferGlobal": true,
|
"preferGlobal": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"hardlounge": "index.js"
|
"hardlounge": "index.js"
|
||||||
@ -54,7 +54,6 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fastify/busboy": "1.0.0",
|
"@fastify/busboy": "1.0.0",
|
||||||
"bcryptjs": "2.4.3",
|
"bcryptjs": "2.4.3",
|
||||||
"caniuse-lite": "^1.0.30001561",
|
|
||||||
"chalk": "4.1.2",
|
"chalk": "4.1.2",
|
||||||
"cheerio": "1.0.0-rc.12",
|
"cheerio": "1.0.0-rc.12",
|
||||||
"commander": "9.0.0",
|
"commander": "9.0.0",
|
||||||
@ -62,7 +61,6 @@
|
|||||||
"express": "4.17.3",
|
"express": "4.17.3",
|
||||||
"file-type": "16.5.4",
|
"file-type": "16.5.4",
|
||||||
"filenamify": "4.3.0",
|
"filenamify": "4.3.0",
|
||||||
"get-func-name": "2.0.2",
|
|
||||||
"got": "11.8.5",
|
"got": "11.8.5",
|
||||||
"irc-framework": "4.13.1",
|
"irc-framework": "4.13.1",
|
||||||
"is-utf8": "0.2.1",
|
"is-utf8": "0.2.1",
|
||||||
@ -93,18 +91,18 @@
|
|||||||
"@istanbuljs/nyc-config-typescript": "1.0.2",
|
"@istanbuljs/nyc-config-typescript": "1.0.2",
|
||||||
"@textcomplete/core": "0.1.10",
|
"@textcomplete/core": "0.1.10",
|
||||||
"@textcomplete/textarea": "0.1.12",
|
"@textcomplete/textarea": "0.1.12",
|
||||||
"@types/bcryptjs": "2.4.5",
|
"@types/bcryptjs": "2.4.4",
|
||||||
"@types/chai": "4.3.5",
|
"@types/chai": "4.3.5",
|
||||||
"@types/cheerio": "0.22.33",
|
"@types/cheerio": "0.22.31",
|
||||||
"@types/content-disposition": "0.5.7",
|
"@types/content-disposition": "0.5.5",
|
||||||
"@types/express": "4.17.13",
|
"@types/express": "4.17.13",
|
||||||
"@types/is-utf8": "0.2.2",
|
"@types/is-utf8": "0.2.1",
|
||||||
"@types/ldapjs": "2.2.2",
|
"@types/ldapjs": "2.2.2",
|
||||||
"@types/linkify-it": "3.0.3",
|
"@types/linkify-it": "3.0.3",
|
||||||
"@types/lodash": "4.14.200",
|
"@types/lodash": "4.14.195",
|
||||||
"@types/mime-types": "2.1.1",
|
"@types/mime-types": "2.1.1",
|
||||||
"@types/mocha": "9.1.1",
|
"@types/mocha": "9.1.1",
|
||||||
"@types/mousetrap": "1.6.13",
|
"@types/mousetrap": "1.6.11",
|
||||||
"@types/node": "17.0.31",
|
"@types/node": "17.0.31",
|
||||||
"@types/read": "0.0.29",
|
"@types/read": "0.0.29",
|
||||||
"@types/semver": "7.3.9",
|
"@types/semver": "7.3.9",
|
||||||
|
@ -112,11 +112,7 @@ class ClientManager {
|
|||||||
if (client) {
|
if (client) {
|
||||||
client.quit(true);
|
client.quit(true);
|
||||||
this.clients = _.without(this.clients, client);
|
this.clients = _.without(this.clients, client);
|
||||||
log.info(
|
log.info(`User ${colors.bold(name)} disconnected and removed.`);
|
||||||
`User ${colors.bold(
|
|
||||||
name
|
|
||||||
)} disconnected and removed.`
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@ -201,8 +197,7 @@ class ClientManager {
|
|||||||
if (
|
if (
|
||||||
userFolderStat &&
|
userFolderStat &&
|
||||||
userFileStat &&
|
userFileStat &&
|
||||||
(userFolderStat.uid !== userFileStat.uid ||
|
(userFolderStat.uid !== userFileStat.uid || userFolderStat.gid !== userFileStat.gid)
|
||||||
userFolderStat.gid !== userFileStat.gid)
|
|
||||||
) {
|
) {
|
||||||
log.warn(
|
log.warn(
|
||||||
`User ${colors.green(
|
`User ${colors.green(
|
||||||
@ -232,10 +227,7 @@ class ClientManager {
|
|||||||
networks: client.networks.map((n) => n.export()),
|
networks: client.networks.map((n) => n.export()),
|
||||||
});
|
});
|
||||||
const newUser = JSON.stringify(json, null, "\t");
|
const newUser = JSON.stringify(json, null, "\t");
|
||||||
const newHash = crypto
|
const newHash = crypto.createHash("sha256").update(newUser).digest("hex");
|
||||||
.createHash("sha256")
|
|
||||||
.update(newUser)
|
|
||||||
.digest("hex");
|
|
||||||
|
|
||||||
return {newUser, newHash};
|
return {newUser, newHash};
|
||||||
}
|
}
|
||||||
@ -262,9 +254,7 @@ class ClientManager {
|
|||||||
return callback ? callback() : true;
|
return callback ? callback() : true;
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
|
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
|
||||||
log.error(
|
log.error(`Failed to update user ${colors.green(client.name)} (${e})`);
|
||||||
`Failed to update user ${colors.green(client.name)} (${e})`
|
|
||||||
);
|
|
||||||
|
|
||||||
if (callback) {
|
if (callback) {
|
||||||
callback(e);
|
callback(e);
|
||||||
@ -276,9 +266,7 @@ class ClientManager {
|
|||||||
const userPath = Config.getUserConfigPath(name);
|
const userPath = Config.getUserConfigPath(name);
|
||||||
|
|
||||||
if (!fs.existsSync(userPath)) {
|
if (!fs.existsSync(userPath)) {
|
||||||
log.error(
|
log.error(`Tried to remove non-existing user ${colors.green(name)}.`);
|
||||||
`Tried to remove non-existing user ${colors.green(name)}.`
|
|
||||||
);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -287,7 +275,7 @@ class ClientManager {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
readUserConfig(name: string) {
|
private readUserConfig(name: string) {
|
||||||
const userPath = Config.getUserConfigPath(name);
|
const userPath = Config.getUserConfigPath(name);
|
||||||
|
|
||||||
if (!fs.existsSync(userPath)) {
|
if (!fs.existsSync(userPath)) {
|
||||||
|
@ -42,7 +42,6 @@ program.addCommand(require("./install").default);
|
|||||||
program.addCommand(require("./uninstall").default);
|
program.addCommand(require("./uninstall").default);
|
||||||
program.addCommand(require("./upgrade").default);
|
program.addCommand(require("./upgrade").default);
|
||||||
program.addCommand(require("./outdated").default);
|
program.addCommand(require("./outdated").default);
|
||||||
program.addCommand(require("./storage").default);
|
|
||||||
|
|
||||||
if (!Config.values.public) {
|
if (!Config.values.public) {
|
||||||
require("./users").default.forEach((command: Command) => {
|
require("./users").default.forEach((command: Command) => {
|
||||||
@ -100,9 +99,7 @@ function verifyFileOwner() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const configStat = fs.statSync(
|
const configStat = fs.statSync(path.join(Config.getHomePath(), "config.js"));
|
||||||
path.join(Config.getHomePath(), "config.js")
|
|
||||||
);
|
|
||||||
|
|
||||||
if (configStat && configStat.uid !== uid) {
|
if (configStat && configStat.uid !== uid) {
|
||||||
log.warn(
|
log.warn(
|
||||||
|
@ -1,68 +0,0 @@
|
|||||||
import log from "../log";
|
|
||||||
import { Command } from "commander";
|
|
||||||
import ClientManager from "../clientManager";
|
|
||||||
import Utils from "./utils";
|
|
||||||
import SqliteMessageStorage from "../plugins/messageStorage/sqlite";
|
|
||||||
|
|
||||||
const program = new Command("storage").description(
|
|
||||||
"various utilities related to the message storage"
|
|
||||||
);
|
|
||||||
|
|
||||||
program
|
|
||||||
.command("migrate")
|
|
||||||
.argument("[user]", "migrate a specific user only, all if not provided")
|
|
||||||
.description("Migrate message storage where needed")
|
|
||||||
.on("--help", Utils.extraHelp)
|
|
||||||
.action(function (user) {
|
|
||||||
runMigrations(user).catch((err) => {
|
|
||||||
log.error(err.toString());
|
|
||||||
process.exit(1);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
async function runMigrations(user: string) {
|
|
||||||
const manager = new ClientManager();
|
|
||||||
const users = manager.getUsers();
|
|
||||||
|
|
||||||
if (user) {
|
|
||||||
if (!users.includes(user)) {
|
|
||||||
throw new Error(`invalid user ${user}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
return migrateUser(manager, user);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const name of users) {
|
|
||||||
await migrateUser(manager, name);
|
|
||||||
// if any migration fails we blow up,
|
|
||||||
// chances are the rest won't complete either
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// runs sqlite migrations for a user, which must exist
|
|
||||||
async function migrateUser(manager: ClientManager, user: string) {
|
|
||||||
log.info("handling user", user);
|
|
||||||
|
|
||||||
if (!isUserLogEnabled(manager, user)) {
|
|
||||||
log.info("logging disabled for user", user, ". Skipping");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const sqlite = new SqliteMessageStorage(user);
|
|
||||||
await sqlite.enable(); // enable runs migrations
|
|
||||||
await sqlite.close();
|
|
||||||
log.info("user", user, "migrated successfully");
|
|
||||||
}
|
|
||||||
|
|
||||||
function isUserLogEnabled(manager: ClientManager, user: string): boolean {
|
|
||||||
const conf = manager.readUserConfig(user);
|
|
||||||
|
|
||||||
if (!conf) {
|
|
||||||
log.error("Could not open user configuration of", user);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return conf.log;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default program;
|
|
@ -7,11 +7,7 @@ import Config from "../../config";
|
|||||||
import Msg, {Message} from "../../models/msg";
|
import Msg, {Message} from "../../models/msg";
|
||||||
import Chan, {Channel} from "../../models/chan";
|
import Chan, {Channel} from "../../models/chan";
|
||||||
import Helper from "../../helper";
|
import Helper from "../../helper";
|
||||||
import type {
|
import type {SearchResponse, SearchQuery, SearchableMessageStorage} from "./types";
|
||||||
SearchResponse,
|
|
||||||
SearchQuery,
|
|
||||||
SearchableMessageStorage,
|
|
||||||
} from "./types";
|
|
||||||
import Network from "../../models/network";
|
import Network from "../../models/network";
|
||||||
|
|
||||||
// TODO; type
|
// TODO; type
|
||||||
@ -20,9 +16,7 @@ let sqlite3: any;
|
|||||||
try {
|
try {
|
||||||
sqlite3 = require("sqlite3");
|
sqlite3 = require("sqlite3");
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
Config.values.messageStorage = Config.values.messageStorage.filter(
|
Config.values.messageStorage = Config.values.messageStorage.filter((item) => item !== "sqlite");
|
||||||
(item) => item !== "sqlite"
|
|
||||||
);
|
|
||||||
|
|
||||||
log.error(
|
log.error(
|
||||||
"Unable to load sqlite3 module. See https://github.com/mapbox/node-sqlite3/wiki/Binaries"
|
"Unable to load sqlite3 module. See https://github.com/mapbox/node-sqlite3/wiki/Binaries"
|
||||||
@ -30,11 +24,7 @@ try {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Migration = {version: number; stmts: string[]};
|
type Migration = {version: number; stmts: string[]};
|
||||||
type Rollback = {
|
type Rollback = {version: number; rollback_forbidden?: boolean; stmts: string[]};
|
||||||
version: number;
|
|
||||||
rollback_forbidden?: boolean;
|
|
||||||
stmts: string[];
|
|
||||||
};
|
|
||||||
|
|
||||||
export const currentSchemaVersion = 1679743888000; // use `new Date().getTime()`
|
export const currentSchemaVersion = 1679743888000; // use `new Date().getTime()`
|
||||||
|
|
||||||
@ -162,10 +152,9 @@ class SqliteMessageStorage implements SearchableMessageStorage {
|
|||||||
await this.serialize_run(stmt, []);
|
await this.serialize_run(stmt, []);
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.serialize_run(
|
await this.serialize_run("INSERT INTO options (name, value) VALUES ('schema_version', ?)", [
|
||||||
"INSERT INTO options (name, value) VALUES ('schema_version', ?)",
|
currentSchemaVersion.toString(),
|
||||||
[currentSchemaVersion.toString()]
|
]);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async current_version(): Promise<number> {
|
async current_version(): Promise<number> {
|
||||||
@ -192,10 +181,9 @@ class SqliteMessageStorage implements SearchableMessageStorage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async update_version_in_db() {
|
async update_version_in_db() {
|
||||||
return this.serialize_run(
|
return this.serialize_run("UPDATE options SET value = ? WHERE name = 'schema_version'", [
|
||||||
"UPDATE options SET value = ? WHERE name = 'schema_version'",
|
currentSchemaVersion.toString(),
|
||||||
[currentSchemaVersion.toString()]
|
]);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async _run_migrations(dbVersion: number) {
|
async _run_migrations(dbVersion: number) {
|
||||||
@ -286,7 +274,7 @@ class SqliteMessageStorage implements SearchableMessageStorage {
|
|||||||
stmts: [raw.statement],
|
stmts: [raw.statement],
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
last.stmts.push(raw.statement);
|
last.stmts.push(raw.statment);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -294,10 +282,7 @@ class SqliteMessageStorage implements SearchableMessageStorage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async delete_migrations_older_than(version: number) {
|
async delete_migrations_older_than(version: number) {
|
||||||
return this.serialize_run(
|
return this.serialize_run("delete from migrations where migrations.version > ?", [version]);
|
||||||
"delete from migrations where migrations.version > ?",
|
|
||||||
[version]
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async _downgrade_to(version: number) {
|
async _downgrade_to(version: number) {
|
||||||
@ -322,7 +307,7 @@ class SqliteMessageStorage implements SearchableMessageStorage {
|
|||||||
await this.delete_migrations_older_than(version);
|
await this.delete_migrations_older_than(version);
|
||||||
await this.update_version_in_db();
|
await this.update_version_in_db();
|
||||||
|
|
||||||
return version;
|
return _rollbacks.at(-1)!.version; // assert valid due to length guard above
|
||||||
}
|
}
|
||||||
|
|
||||||
async downgrade_to(version: number) {
|
async downgrade_to(version: number) {
|
||||||
@ -387,12 +372,7 @@ class SqliteMessageStorage implements SearchableMessageStorage {
|
|||||||
// id is regenerated when messages are retrieved
|
// id is regenerated when messages are retrieved
|
||||||
// previews are not stored because storage is cleared on lounge restart
|
// previews are not stored because storage is cleared on lounge restart
|
||||||
// type and time are stored in a separate column
|
// type and time are stored in a separate column
|
||||||
if (
|
if (prop !== "id" && prop !== "previews" && prop !== "type" && prop !== "time") {
|
||||||
prop !== "id" &&
|
|
||||||
prop !== "previews" &&
|
|
||||||
prop !== "type" &&
|
|
||||||
prop !== "time"
|
|
||||||
) {
|
|
||||||
newMsg[prop] = msg[prop];
|
newMsg[prop] = msg[prop];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -418,10 +398,10 @@ class SqliteMessageStorage implements SearchableMessageStorage {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.serialize_run(
|
await this.serialize_run("DELETE FROM messages WHERE network = ? AND channel = ?", [
|
||||||
"DELETE FROM messages WHERE network = ? AND channel = ?",
|
network.uuid,
|
||||||
[network.uuid, channel.name.toLowerCase()]
|
channel.name.toLowerCase(),
|
||||||
);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
async getMessages(
|
async getMessages(
|
||||||
@ -436,8 +416,7 @@ class SqliteMessageStorage implements SearchableMessageStorage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If unlimited history is specified, load 100k messages
|
// If unlimited history is specified, load 100k messages
|
||||||
const limit =
|
const limit = Config.values.maxHistory < 0 ? 100000 : Config.values.maxHistory;
|
||||||
Config.values.maxHistory < 0 ? 100000 : Config.values.maxHistory;
|
|
||||||
|
|
||||||
const rows = await this.serialize_fetchall(
|
const rows = await this.serialize_fetchall(
|
||||||
"SELECT msg, type, time FROM messages WHERE network = ? AND channel = ? ORDER BY time DESC LIMIT ?",
|
"SELECT msg, type, time FROM messages WHERE network = ? AND channel = ? ORDER BY time DESC LIMIT ?",
|
||||||
|
171
server/server.ts
171
server/server.ts
@ -72,9 +72,9 @@ export default async function (
|
|||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
log.info(`Hard Lounge ${colors.green(Helper.getVersion())} \
|
log.info(`Hard Lounge ${colors.green(Helper.getVersion())} \
|
||||||
(Node.js ${colors.green(process.versions.node)} on ${colors.green(
|
(Node.js ${colors.green(process.versions.node)} on ${colors.green(process.platform)} ${
|
||||||
process.platform
|
process.arch
|
||||||
)} ${process.arch})`);
|
})`);
|
||||||
log.info(`Configuration file: ${colors.green(Config.getConfigPath())}`);
|
log.info(`Configuration file: ${colors.green(Config.getConfigPath())}`);
|
||||||
|
|
||||||
const staticOptions = {
|
const staticOptions = {
|
||||||
@ -96,16 +96,8 @@ export default async function (
|
|||||||
.get("/service-worker.js", forceNoCacheRequest)
|
.get("/service-worker.js", forceNoCacheRequest)
|
||||||
.get("/js/bundle.js.map", forceNoCacheRequest)
|
.get("/js/bundle.js.map", forceNoCacheRequest)
|
||||||
.get("/css/style.css.map", forceNoCacheRequest)
|
.get("/css/style.css.map", forceNoCacheRequest)
|
||||||
.use(
|
.use(express.static(Utils.getFileFromRelativeToRoot("public"), staticOptions))
|
||||||
express.static(
|
.use("/storage/", express.static(Config.getStoragePath(), staticOptions));
|
||||||
Utils.getFileFromRelativeToRoot("public"),
|
|
||||||
staticOptions
|
|
||||||
)
|
|
||||||
)
|
|
||||||
.use(
|
|
||||||
"/storage/",
|
|
||||||
express.static(Config.getStoragePath(), staticOptions)
|
|
||||||
);
|
|
||||||
|
|
||||||
if (Config.values.fileUpload.enable) {
|
if (Config.values.fileUpload.enable) {
|
||||||
Uploader.router(app);
|
Uploader.router(app);
|
||||||
@ -131,10 +123,7 @@ export default async function (
|
|||||||
const fileName = req.params.filename;
|
const fileName = req.params.filename;
|
||||||
const packageFile = packages.getPackage(packageName);
|
const packageFile = packages.getPackage(packageName);
|
||||||
|
|
||||||
if (
|
if (!packageFile || !packages.getFiles().includes(`${packageName}/${fileName}`)) {
|
||||||
!packageFile ||
|
|
||||||
!packages.getFiles().includes(`${packageName}/${fileName}`)
|
|
||||||
) {
|
|
||||||
return res.status(404).send("Not found");
|
return res.status(404).send("Not found");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -191,10 +180,7 @@ export default async function (
|
|||||||
host: string | undefined;
|
host: string | undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
if (
|
if (typeof Config.values.host === "string" && Config.values.host.startsWith("unix:")) {
|
||||||
typeof Config.values.host === "string" &&
|
|
||||||
Config.values.host.startsWith("unix:")
|
|
||||||
) {
|
|
||||||
listenParams = Config.values.host.replace(/^unix:/, "");
|
listenParams = Config.values.host.replace(/^unix:/, "");
|
||||||
} else {
|
} else {
|
||||||
listenParams = {
|
listenParams = {
|
||||||
@ -222,12 +208,8 @@ export default async function (
|
|||||||
|
|
||||||
log.info(
|
log.info(
|
||||||
"Available at " +
|
"Available at " +
|
||||||
colors.green(
|
colors.green(`${protocol}://${address.address}:${address.port}/`) +
|
||||||
`${protocol}://${address.address}:${address.port}/`
|
` in ${colors.bold(Config.values.public ? "public" : "private")} mode`
|
||||||
) +
|
|
||||||
` in ${colors.bold(
|
|
||||||
Config.values.public ? "public" : "private"
|
|
||||||
)} mode`
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -285,9 +267,7 @@ export default async function (
|
|||||||
log.error(`Could not start identd server, ${err.message}`);
|
log.error(`Could not start identd server, ${err.message}`);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
} else if (!manager) {
|
} else if (!manager) {
|
||||||
log.error(
|
log.error("Could not start identd server, ClientManager is undefined");
|
||||||
"Could not start identd server, ClientManager is undefined"
|
|
||||||
);
|
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -310,9 +290,7 @@ export default async function (
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (Config.values.prefetchStorage) {
|
if (Config.values.prefetchStorage) {
|
||||||
log.info(
|
log.info("Clearing prefetch storage folder, this might take a while...");
|
||||||
"Clearing prefetch storage folder, this might take a while..."
|
|
||||||
);
|
|
||||||
|
|
||||||
(await import("./plugins/storage")).default.emptyDir();
|
(await import("./plugins/storage")).default.emptyDir();
|
||||||
}
|
}
|
||||||
@ -355,10 +333,7 @@ export default async function (
|
|||||||
function getClientLanguage(socket: Socket): string | null {
|
function getClientLanguage(socket: Socket): string | null {
|
||||||
const acceptLanguage = socket.handshake.headers["accept-language"];
|
const acceptLanguage = socket.handshake.headers["accept-language"];
|
||||||
|
|
||||||
if (
|
if (typeof acceptLanguage === "string" && /^[\x00-\x7F]{1,50}$/.test(acceptLanguage)) {
|
||||||
typeof acceptLanguage === "string" &&
|
|
||||||
/^[\x00-\x7F]{1,50}$/.test(acceptLanguage)
|
|
||||||
) {
|
|
||||||
// only allow ASCII strings between 1-50 characters in length
|
// only allow ASCII strings between 1-50 characters in length
|
||||||
return acceptLanguage;
|
return acceptLanguage;
|
||||||
}
|
}
|
||||||
@ -385,10 +360,7 @@ function getClientIp(socket: Socket) {
|
|||||||
function getClientSecure(socket: Socket) {
|
function getClientSecure(socket: Socket) {
|
||||||
let secure = socket.handshake.secure;
|
let secure = socket.handshake.secure;
|
||||||
|
|
||||||
if (
|
if (Config.values.reverseProxy && socket.handshake.headers["x-forwarded-proto"] === "https") {
|
||||||
Config.values.reverseProxy &&
|
|
||||||
socket.handshake.headers["x-forwarded-proto"] === "https"
|
|
||||||
) {
|
|
||||||
secure = true;
|
secure = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -418,9 +390,7 @@ function addSecurityHeaders(req: Request, res: Response, next: NextFunction) {
|
|||||||
// - https://user-images.githubusercontent.com is where we currently push our changelog screenshots
|
// - https://user-images.githubusercontent.com is where we currently push our changelog screenshots
|
||||||
// - data: is required for the HTML5 video player
|
// - data: is required for the HTML5 video player
|
||||||
if (Config.values.prefetchStorage || !Config.values.prefetch) {
|
if (Config.values.prefetchStorage || !Config.values.prefetch) {
|
||||||
policies.push(
|
policies.push("img-src 'self' data: https://user-images.githubusercontent.com");
|
||||||
"img-src 'self' data: https://user-images.githubusercontent.com"
|
|
||||||
);
|
|
||||||
policies.unshift("block-all-mixed-content");
|
policies.unshift("block-all-mixed-content");
|
||||||
} else {
|
} else {
|
||||||
policies.push("img-src http: https: data:");
|
policies.push("img-src http: https: data:");
|
||||||
@ -479,9 +449,7 @@ function initializeClient(
|
|||||||
|
|
||||||
// If client provided channel passes checks, use it. if client has invalid
|
// If client provided channel passes checks, use it. if client has invalid
|
||||||
// channel open (or windows like settings) then use last known server active channel
|
// channel open (or windows like settings) then use last known server active channel
|
||||||
openChannel =
|
openChannel = client.attachedClients[socket.id].openChannel || client.lastActiveChannel;
|
||||||
client.attachedClients[socket.id].openChannel ||
|
|
||||||
client.lastActiveChannel;
|
|
||||||
} else {
|
} else {
|
||||||
openChannel = client.lastActiveChannel;
|
openChannel = client.lastActiveChannel;
|
||||||
}
|
}
|
||||||
@ -584,10 +552,7 @@ function initializeClient(
|
|||||||
const hash = Helper.password.hash(p1);
|
const hash = Helper.password.hash(p1);
|
||||||
|
|
||||||
client.setPassword(hash, (success: boolean) => {
|
client.setPassword(hash, (success: boolean) => {
|
||||||
const obj = {
|
const obj = {success: false, error: undefined} as {
|
||||||
success: false,
|
|
||||||
error: undefined,
|
|
||||||
} as {
|
|
||||||
success: boolean;
|
success: boolean;
|
||||||
error: string | undefined;
|
error: string | undefined;
|
||||||
};
|
};
|
||||||
@ -602,9 +567,7 @@ function initializeClient(
|
|||||||
});
|
});
|
||||||
})
|
})
|
||||||
.catch((error: Error) => {
|
.catch((error: Error) => {
|
||||||
log.error(
|
log.error(`Error while checking users password. Error: ${error.message}`);
|
||||||
`Error while checking users password. Error: ${error.message}`
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -633,9 +596,7 @@ function initializeClient(
|
|||||||
socket.emit("changelog", changelogData);
|
socket.emit("changelog", changelogData);
|
||||||
})
|
})
|
||||||
.catch((error: Error) => {
|
.catch((error: Error) => {
|
||||||
log.error(
|
log.error(`Error while fetching changelog. Error: ${error.message}`);
|
||||||
`Error while fetching changelog. Error: ${error.message}`
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -704,12 +665,7 @@ function initializeClient(
|
|||||||
|
|
||||||
if (!Config.values.public) {
|
if (!Config.values.public) {
|
||||||
socket.on("push:register", (subscription) => {
|
socket.on("push:register", (subscription) => {
|
||||||
if (
|
if (!Object.prototype.hasOwnProperty.call(client.config.sessions, token)) {
|
||||||
!Object.prototype.hasOwnProperty.call(
|
|
||||||
client.config.sessions,
|
|
||||||
token
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -728,23 +684,18 @@ function initializeClient(
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
socket.on("push:unregister", () =>
|
socket.on("push:unregister", () => client.unregisterPushSubscription(token));
|
||||||
client.unregisterPushSubscription(token)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const sendSessionList = () => {
|
const sendSessionList = () => {
|
||||||
// TODO: this should use the ClientSession type currently in client
|
// TODO: this should use the ClientSession type currently in client
|
||||||
const sessions = _.map(
|
const sessions = _.map(client.config.sessions, (session, sessionToken) => {
|
||||||
client.config.sessions,
|
|
||||||
(session, sessionToken) => {
|
|
||||||
return {
|
return {
|
||||||
current: sessionToken === token,
|
current: sessionToken === token,
|
||||||
active: _.reduce(
|
active: _.reduce(
|
||||||
client.attachedClients,
|
client.attachedClients,
|
||||||
(count, attachedClient) =>
|
(count, attachedClient) =>
|
||||||
count +
|
count + (attachedClient.token === sessionToken ? 1 : 0),
|
||||||
(attachedClient.token === sessionToken ? 1 : 0),
|
|
||||||
0
|
0
|
||||||
),
|
),
|
||||||
lastUse: session.lastUse,
|
lastUse: session.lastUse,
|
||||||
@ -752,8 +703,7 @@ function initializeClient(
|
|||||||
agent: session.agent,
|
agent: session.agent,
|
||||||
token: sessionToken, // TODO: Ideally don't expose actual tokens to the client
|
token: sessionToken, // TODO: Ideally don't expose actual tokens to the client
|
||||||
};
|
};
|
||||||
}
|
});
|
||||||
);
|
|
||||||
|
|
||||||
socket.emit("sessions:list", sessions);
|
socket.emit("sessions:list", sessions);
|
||||||
};
|
};
|
||||||
@ -775,12 +725,8 @@ function initializeClient(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// We do not need to do write operations and emit events if nothing changed.
|
// We do not need to do write operations and emit events if nothing changed.
|
||||||
if (
|
if (client.config.clientSettings[newSetting.name] !== newSetting.value) {
|
||||||
client.config.clientSettings[newSetting.name] !==
|
client.config.clientSettings[newSetting.name] = newSetting.value;
|
||||||
newSetting.value
|
|
||||||
) {
|
|
||||||
client.config.clientSettings[newSetting.name] =
|
|
||||||
newSetting.value;
|
|
||||||
|
|
||||||
// Pass the setting to all clients.
|
// Pass the setting to all clients.
|
||||||
client.emit("setting:new", {
|
client.emit("setting:new", {
|
||||||
@ -790,10 +736,7 @@ function initializeClient(
|
|||||||
|
|
||||||
client.save();
|
client.save();
|
||||||
|
|
||||||
if (
|
if (newSetting.name === "highlights" || newSetting.name === "highlightExceptions") {
|
||||||
newSetting.name === "highlights" ||
|
|
||||||
newSetting.name === "highlightExceptions"
|
|
||||||
) {
|
|
||||||
client.compileCustomHighlights();
|
client.compileCustomHighlights();
|
||||||
} else if (newSetting.name === "awayMessage") {
|
} else if (newSetting.name === "awayMessage") {
|
||||||
if (typeof newSetting.value !== "string") {
|
if (typeof newSetting.value !== "string") {
|
||||||
@ -806,12 +749,7 @@ function initializeClient(
|
|||||||
});
|
});
|
||||||
|
|
||||||
socket.on("setting:get", () => {
|
socket.on("setting:get", () => {
|
||||||
if (
|
if (!Object.prototype.hasOwnProperty.call(client.config, "clientSettings")) {
|
||||||
!Object.prototype.hasOwnProperty.call(
|
|
||||||
client.config,
|
|
||||||
"clientSettings"
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
socket.emit("setting:all", {});
|
socket.emit("setting:all", {});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -864,12 +802,7 @@ function initializeClient(
|
|||||||
tokenToSignOut = token;
|
tokenToSignOut = token;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (!Object.prototype.hasOwnProperty.call(client.config.sessions, tokenToSignOut)) {
|
||||||
!Object.prototype.hasOwnProperty.call(
|
|
||||||
client.config.sessions,
|
|
||||||
tokenToSignOut
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -882,9 +815,7 @@ function initializeClient(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const socketToRemove = manager!.sockets
|
const socketToRemove = manager!.sockets.of("/").sockets.get(socketId);
|
||||||
.of("/")
|
|
||||||
.sockets.get(socketId);
|
|
||||||
|
|
||||||
socketToRemove!.emit("sign-out");
|
socketToRemove!.emit("sign-out");
|
||||||
socketToRemove!.disconnect();
|
socketToRemove!.disconnect();
|
||||||
@ -983,13 +914,7 @@ function performAuthentication(this: Socket, data) {
|
|||||||
let token: string;
|
let token: string;
|
||||||
|
|
||||||
const finalInit = () =>
|
const finalInit = () =>
|
||||||
initializeClient(
|
initializeClient(socket, client, token, data.lastMessage || -1, data.openChannel);
|
||||||
socket,
|
|
||||||
client,
|
|
||||||
token,
|
|
||||||
data.lastMessage || -1,
|
|
||||||
data.openChannel
|
|
||||||
);
|
|
||||||
|
|
||||||
const initClient = () => {
|
const initClient = () => {
|
||||||
// Configuration does not change during runtime of TL,
|
// Configuration does not change during runtime of TL,
|
||||||
@ -999,9 +924,7 @@ function performAuthentication(this: Socket, data) {
|
|||||||
|
|
||||||
socket.emit(
|
socket.emit(
|
||||||
"push:issubscribed",
|
"push:issubscribed",
|
||||||
token && client.config.sessions[token].pushSubscription
|
token && client.config.sessions[token].pushSubscription ? true : false
|
||||||
? true
|
|
||||||
: false
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1053,9 +976,9 @@ function performAuthentication(this: Socket, data) {
|
|||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
log.warn(
|
log.warn(
|
||||||
`Authentication failed for user ${colors.bold(
|
`Authentication failed for user ${colors.bold(data.user)} from ${colors.bold(
|
||||||
data.user
|
getClientIp(socket)
|
||||||
)} from ${colors.bold(getClientIp(socket))}`
|
)}`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1078,12 +1001,7 @@ function performAuthentication(this: Socket, data) {
|
|||||||
if (client && data.token) {
|
if (client && data.token) {
|
||||||
const providedToken = client.calculateTokenHash(data.token);
|
const providedToken = client.calculateTokenHash(data.token);
|
||||||
|
|
||||||
if (
|
if (Object.prototype.hasOwnProperty.call(client.config.sessions, providedToken)) {
|
||||||
Object.prototype.hasOwnProperty.call(
|
|
||||||
client.config.sessions,
|
|
||||||
providedToken
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
token = providedToken;
|
token = providedToken;
|
||||||
|
|
||||||
return authCallback(true);
|
return authCallback(true);
|
||||||
@ -1097,19 +1015,12 @@ function performAuthentication(this: Socket, data) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function reverseDnsLookup(ip: string, callback: (hostname: string) => void) {
|
function reverseDnsLookup(ip: string, callback: (hostname: string) => void) {
|
||||||
// node can throw, even if we provide valid input based on the DNS server
|
|
||||||
// returning SERVFAIL it seems: https://github.com/thelounge/thelounge/issues/4768
|
|
||||||
// so we manually resolve with the ip as a fallback in case something fails
|
|
||||||
try {
|
|
||||||
dns.reverse(ip, (reverseErr, hostnames) => {
|
dns.reverse(ip, (reverseErr, hostnames) => {
|
||||||
if (reverseErr || hostnames.length < 1) {
|
if (reverseErr || hostnames.length < 1) {
|
||||||
return callback(ip);
|
return callback(ip);
|
||||||
}
|
}
|
||||||
|
|
||||||
dns.resolve(
|
dns.resolve(hostnames[0], net.isIP(ip) === 6 ? "AAAA" : "A", (resolveErr, resolvedIps) => {
|
||||||
hostnames[0],
|
|
||||||
net.isIP(ip) === 6 ? "AAAA" : "A",
|
|
||||||
(resolveErr, resolvedIps) => {
|
|
||||||
// TODO: investigate SoaRecord class
|
// TODO: investigate SoaRecord class
|
||||||
if (!Array.isArray(resolvedIps)) {
|
if (!Array.isArray(resolvedIps)) {
|
||||||
return callback(ip);
|
return callback(ip);
|
||||||
@ -1126,14 +1037,6 @@ function reverseDnsLookup(ip: string, callback: (hostname: string) => void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return callback(ip);
|
return callback(ip);
|
||||||
}
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
} catch (err) {
|
});
|
||||||
log.error(
|
|
||||||
`failed to resolve rDNS for ${ip}, using ip instead`,
|
|
||||||
(err as any).toString()
|
|
||||||
);
|
|
||||||
setImmediate(callback, ip); // makes sure we always behave asynchronously
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -111,7 +111,7 @@ describe("SQLite migrations", function () {
|
|||||||
it("has working down-migrations", async function () {
|
it("has working down-migrations", async function () {
|
||||||
await serialize_run("BEGIN EXCLUSIVE TRANSACTION");
|
await serialize_run("BEGIN EXCLUSIVE TRANSACTION");
|
||||||
|
|
||||||
for (const rollback of rollbacks.slice().reverse()) {
|
for (const rollback of rollbacks.reverse()) {
|
||||||
if (rollback.rollback_forbidden) {
|
if (rollback.rollback_forbidden) {
|
||||||
throw Error(
|
throw Error(
|
||||||
"Try to write a down migration, if you really can't, flip this to a break"
|
"Try to write a down migration, if you really can't, flip this to a break"
|
||||||
@ -132,11 +132,7 @@ describe("SQLite Message Storage", function () {
|
|||||||
this.timeout(util.isRunningOnCI() ? 25000 : 5000);
|
this.timeout(util.isRunningOnCI() ? 25000 : 5000);
|
||||||
this.slow(300);
|
this.slow(300);
|
||||||
|
|
||||||
const expectedPath = path.join(
|
const expectedPath = path.join(Config.getHomePath(), "logs", "testUser.sqlite3");
|
||||||
Config.getHomePath(),
|
|
||||||
"logs",
|
|
||||||
"testUser.sqlite3"
|
|
||||||
);
|
|
||||||
let store: MessageStorage;
|
let store: MessageStorage;
|
||||||
|
|
||||||
function db_get_one(stmt: string, ...params: any[]): Promise<any> {
|
function db_get_one(stmt: string, ...params: any[]): Promise<any> {
|
||||||
@ -197,19 +193,13 @@ describe("SQLite Message Storage", function () {
|
|||||||
|
|
||||||
it("should resolve an empty array when disabled", async function () {
|
it("should resolve an empty array when disabled", async function () {
|
||||||
store.isEnabled = false;
|
store.isEnabled = false;
|
||||||
const messages = await store.getMessages(
|
const messages = await store.getMessages(null as any, null as any, null as any);
|
||||||
null as any,
|
|
||||||
null as any,
|
|
||||||
null as any
|
|
||||||
);
|
|
||||||
expect(messages).to.be.empty;
|
expect(messages).to.be.empty;
|
||||||
store.isEnabled = true;
|
store.isEnabled = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should insert schema version to options table", async function () {
|
it("should insert schema version to options table", async function () {
|
||||||
const row = await db_get_one(
|
const row = await db_get_one("SELECT value FROM options WHERE name = 'schema_version'");
|
||||||
"SELECT value FROM options WHERE name = 'schema_version'"
|
|
||||||
);
|
|
||||||
expect(row.value).to.equal(currentSchemaVersion.toString());
|
expect(row.value).to.equal(currentSchemaVersion.toString());
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -278,10 +268,7 @@ describe("SQLite Message Storage", function () {
|
|||||||
() => msgId++
|
() => msgId++
|
||||||
);
|
);
|
||||||
expect(messages).to.have.lengthOf(2);
|
expect(messages).to.have.lengthOf(2);
|
||||||
expect(messages.map((i_1) => i_1.text)).to.deep.equal([
|
expect(messages.map((i_1) => i_1.text)).to.deep.equal(["msg 198", "msg 199"]);
|
||||||
"msg 198",
|
|
||||||
"msg 199",
|
|
||||||
]);
|
|
||||||
} finally {
|
} finally {
|
||||||
Config.values.maxHistory = originalMaxHistory;
|
Config.values.maxHistory = originalMaxHistory;
|
||||||
}
|
}
|
||||||
@ -306,9 +293,7 @@ describe("SQLite Message Storage", function () {
|
|||||||
expectedMessages.push(`msg ${i}`);
|
expectedMessages.push(`msg ${i}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
expect(search.results.map((i_1) => i_1.text)).to.deep.equal(
|
expect(search.results.map((i_1) => i_1.text)).to.deep.equal(expectedMessages);
|
||||||
expectedMessages
|
|
||||||
);
|
|
||||||
} finally {
|
} finally {
|
||||||
Config.values.maxHistory = originalMaxHistory;
|
Config.values.maxHistory = originalMaxHistory;
|
||||||
}
|
}
|
||||||
@ -371,7 +356,7 @@ describe("SQLite Message Storage", function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("should be able to downgrade", async function () {
|
it("should be able to downgrade", async function () {
|
||||||
for (const rollback of rollbacks.slice().reverse()) {
|
for (const rollback of rollbacks.reverse()) {
|
||||||
if (rollback.rollback_forbidden) {
|
if (rollback.rollback_forbidden) {
|
||||||
throw Error(
|
throw Error(
|
||||||
"Try to write a down migration, if you really can't, flip this to a break"
|
"Try to write a down migration, if you really can't, flip this to a break"
|
||||||
|
61
yarn.lock
61
yarn.lock
@ -1328,10 +1328,10 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.4.tgz#0b92dcc0cc1c81f6f306a381f28e31b1a56536e9"
|
resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.4.tgz#0b92dcc0cc1c81f6f306a381f28e31b1a56536e9"
|
||||||
integrity sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==
|
integrity sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==
|
||||||
|
|
||||||
"@types/bcryptjs@2.4.5":
|
"@types/bcryptjs@2.4.4":
|
||||||
version "2.4.5"
|
version "2.4.4"
|
||||||
resolved "https://registry.yarnpkg.com/@types/bcryptjs/-/bcryptjs-2.4.5.tgz#15473cc012f825b3435b189376f645bdd2fc9f11"
|
resolved "https://registry.yarnpkg.com/@types/bcryptjs/-/bcryptjs-2.4.4.tgz#cd3c4c007f600f1d21db09c9bd4ced8b49d04670"
|
||||||
integrity sha512-tOF6TivOIvq+TWQm78335CMdyVJhpBG3NUdWQDAp95ax4E2rSKbws/ELHLk5EBoucwx/tHt3/hhLOHwWJgVrSw==
|
integrity sha512-9wlJI7k5gRyJEC4yrV7DubzNQFTPiykYxUA6lBtsk5NlOfW9oWLJ1HdIA4YtE+6C3i3mTpDQQEosJ2rVZfBWnw==
|
||||||
|
|
||||||
"@types/body-parser@*":
|
"@types/body-parser@*":
|
||||||
version "1.19.3"
|
version "1.19.3"
|
||||||
@ -1361,10 +1361,10 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.3.5.tgz#ae69bcbb1bebb68c4ac0b11e9d8ed04526b3562b"
|
resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.3.5.tgz#ae69bcbb1bebb68c4ac0b11e9d8ed04526b3562b"
|
||||||
integrity sha512-mEo1sAde+UCE6b2hxn332f1g1E8WfYRu6p5SvTKr2ZKC1f7gFJXk4h5PyGP9Dt6gCaG8y8XhwnXWC6Iy2cmBng==
|
integrity sha512-mEo1sAde+UCE6b2hxn332f1g1E8WfYRu6p5SvTKr2ZKC1f7gFJXk4h5PyGP9Dt6gCaG8y8XhwnXWC6Iy2cmBng==
|
||||||
|
|
||||||
"@types/cheerio@0.22.33":
|
"@types/cheerio@0.22.31":
|
||||||
version "0.22.33"
|
version "0.22.31"
|
||||||
resolved "https://registry.yarnpkg.com/@types/cheerio/-/cheerio-0.22.33.tgz#e4792408b107384d7d7469e3b4d31408078ec620"
|
resolved "https://registry.yarnpkg.com/@types/cheerio/-/cheerio-0.22.31.tgz#b8538100653d6bb1b08a1e46dec75b4f2a5d5eb6"
|
||||||
integrity sha512-XUlu2BK4q3xJsccRLK69m/cABZd7m60o+cDEPUTG6jTpuG2vqN35UioeF99MQ/HoSOEPq0Bgil8g3jtzE0oH9A==
|
integrity sha512-Kt7Cdjjdi2XWSfrZ53v4Of0wG3ZcmaegFXjMmz9tfNrZSkzzo36G0AL1YqSdcIA78Etjt6E609pt5h1xnQkPUw==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@types/node" "*"
|
"@types/node" "*"
|
||||||
|
|
||||||
@ -1375,10 +1375,10 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@types/node" "*"
|
"@types/node" "*"
|
||||||
|
|
||||||
"@types/content-disposition@0.5.7":
|
"@types/content-disposition@0.5.5":
|
||||||
version "0.5.7"
|
version "0.5.5"
|
||||||
resolved "https://registry.yarnpkg.com/@types/content-disposition/-/content-disposition-0.5.7.tgz#3b98d4bf8c80640f93b042511acb5aad18139748"
|
resolved "https://registry.yarnpkg.com/@types/content-disposition/-/content-disposition-0.5.5.tgz#650820e95de346e1f84e30667d168c8fd25aa6e3"
|
||||||
integrity sha512-V9/5u21RHFR1zfdm3rQ6pJUKV+zSSVQt+yq16i1YhdivVzWgPEoKedc3GdT8aFjsqQbakdxuy3FnEdePUQOamQ==
|
integrity sha512-v6LCdKfK6BwcqMo+wYW05rLS12S0ZO0Fl4w1h4aaZMD7bqT3gVUns6FvLJKGZHQmYn3SX55JWGpziwJRwVgutA==
|
||||||
|
|
||||||
"@types/cookie@^0.4.1":
|
"@types/cookie@^0.4.1":
|
||||||
version "0.4.1"
|
version "0.4.1"
|
||||||
@ -1448,10 +1448,10 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@types/http-errors/-/http-errors-2.0.2.tgz#a86e00bbde8950364f8e7846687259ffcd96e8c2"
|
resolved "https://registry.yarnpkg.com/@types/http-errors/-/http-errors-2.0.2.tgz#a86e00bbde8950364f8e7846687259ffcd96e8c2"
|
||||||
integrity sha512-lPG6KlZs88gef6aD85z3HNkztpj7w2R7HmR3gygjfXCQmsLloWNARFkMuzKiiY8FGdh1XDpgBdrSf4aKDiA7Kg==
|
integrity sha512-lPG6KlZs88gef6aD85z3HNkztpj7w2R7HmR3gygjfXCQmsLloWNARFkMuzKiiY8FGdh1XDpgBdrSf4aKDiA7Kg==
|
||||||
|
|
||||||
"@types/is-utf8@0.2.2":
|
"@types/is-utf8@0.2.1":
|
||||||
version "0.2.2"
|
version "0.2.1"
|
||||||
resolved "https://registry.yarnpkg.com/@types/is-utf8/-/is-utf8-0.2.2.tgz#b31ab599ead973992809b0b802ce066abbb42efd"
|
resolved "https://registry.yarnpkg.com/@types/is-utf8/-/is-utf8-0.2.1.tgz#2cecf393ce44a73d3d052224e8375709098b4d25"
|
||||||
integrity sha512-j7PFtO0ki4IoJvmMaAHQ70z74Td244dMLC7BAz5pb0v7IC8xXLtuM+7AWsMco4Minz92m30fO72+TbkmtMr4dQ==
|
integrity sha512-4tSeTnvbhBsWZy+NTB7g3mbRDZKN0tgS199YlY2JngABhpxSlKyaUX65Lxw8VnLa6IG4tHxBMi0ffhnFhio7jw==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@types/node" "*"
|
"@types/node" "*"
|
||||||
|
|
||||||
@ -1479,10 +1479,10 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@types/linkify-it/-/linkify-it-3.0.3.tgz#15a0712296c5041733c79efe233ba17ae5a7587b"
|
resolved "https://registry.yarnpkg.com/@types/linkify-it/-/linkify-it-3.0.3.tgz#15a0712296c5041733c79efe233ba17ae5a7587b"
|
||||||
integrity sha512-pTjcqY9E4nOI55Wgpz7eiI8+LzdYnw3qxXCfHyBDdPbYvbyLgWLJGh8EdPvqawwMK1Uo1794AUkkR38Fr0g+2g==
|
integrity sha512-pTjcqY9E4nOI55Wgpz7eiI8+LzdYnw3qxXCfHyBDdPbYvbyLgWLJGh8EdPvqawwMK1Uo1794AUkkR38Fr0g+2g==
|
||||||
|
|
||||||
"@types/lodash@4.14.200":
|
"@types/lodash@4.14.195":
|
||||||
version "4.14.200"
|
version "4.14.195"
|
||||||
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.200.tgz#435b6035c7eba9cdf1e039af8212c9e9281e7149"
|
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.195.tgz#bafc975b252eb6cea78882ce8a7b6bf22a6de632"
|
||||||
integrity sha512-YI/M/4HRImtNf3pJgbF+W6FrXovqj+T+/HpENLTooK9PnkacBsDpeP3IpHab40CClUfhNmdM2WTNP2sa2dni5Q==
|
integrity sha512-Hwx9EUgdwf2GLarOjQp5ZH8ZmblzcbTBC2wtQWNKARBSxM9ezRIAUpeDTgoQRAFB0+8CNWXVA9+MaSOzOF3nPg==
|
||||||
|
|
||||||
"@types/mime-types@2.1.1":
|
"@types/mime-types@2.1.1":
|
||||||
version "2.1.1"
|
version "2.1.1"
|
||||||
@ -1514,10 +1514,10 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-9.1.1.tgz#e7c4f1001eefa4b8afbd1eee27a237fee3bf29c4"
|
resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-9.1.1.tgz#e7c4f1001eefa4b8afbd1eee27a237fee3bf29c4"
|
||||||
integrity sha512-Z61JK7DKDtdKTWwLeElSEBcWGRLY8g95ic5FoQqI9CMx0ns/Ghep3B4DfcEimiKMvtamNVULVNKEsiwV3aQmXw==
|
integrity sha512-Z61JK7DKDtdKTWwLeElSEBcWGRLY8g95ic5FoQqI9CMx0ns/Ghep3B4DfcEimiKMvtamNVULVNKEsiwV3aQmXw==
|
||||||
|
|
||||||
"@types/mousetrap@1.6.13":
|
"@types/mousetrap@1.6.11":
|
||||||
version "1.6.13"
|
version "1.6.11"
|
||||||
resolved "https://registry.yarnpkg.com/@types/mousetrap/-/mousetrap-1.6.13.tgz#1b2e4cd374fdd1ee58a240be0aafd94f7270b3be"
|
resolved "https://registry.yarnpkg.com/@types/mousetrap/-/mousetrap-1.6.11.tgz#ef9620160fdcefcb85bccda8aaa3e84d7429376d"
|
||||||
integrity sha512-dEzDpaR+P/thkMsjsREQDX9OP8AMyLncTkgUgTTIxq5lJTlQffiLJt67ImDtaX+kC7CaNIX30pfdrrMZkym+eg==
|
integrity sha512-F0oAily9Q9QQpv9JKxKn0zMKfOo36KHCW7myYsmUyf2t0g+sBTbG3UleTPoguHdE1z3GLFr3p7/wiOio52QFjQ==
|
||||||
|
|
||||||
"@types/node@*", "@types/node@>=10.0.0":
|
"@types/node@*", "@types/node@>=10.0.0":
|
||||||
version "20.8.4"
|
version "20.8.4"
|
||||||
@ -2779,11 +2779,6 @@ caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001538, caniuse-lite@^1.0.30001541:
|
|||||||
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001546.tgz#10fdad03436cfe3cc632d3af7a99a0fb497407f0"
|
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001546.tgz#10fdad03436cfe3cc632d3af7a99a0fb497407f0"
|
||||||
integrity sha512-zvtSJwuQFpewSyRrI3AsftF6rM0X80mZkChIt1spBGEvRglCrjTniXvinc8JKRoqTwXAgvqTImaN9igfSMtUBw==
|
integrity sha512-zvtSJwuQFpewSyRrI3AsftF6rM0X80mZkChIt1spBGEvRglCrjTniXvinc8JKRoqTwXAgvqTImaN9igfSMtUBw==
|
||||||
|
|
||||||
caniuse-lite@^1.0.30001561:
|
|
||||||
version "1.0.30001565"
|
|
||||||
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001565.tgz#a528b253c8a2d95d2b415e11d8b9942acc100c4f"
|
|
||||||
integrity sha512-xrE//a3O7TP0vaJ8ikzkD2c2NgcVUvsEe2IvFTntV4Yd1Z9FVzh+gW+enX96L0psrbaFMcVcH2l90xNuGDWc8w==
|
|
||||||
|
|
||||||
chai@4.3.7:
|
chai@4.3.7:
|
||||||
version "4.3.7"
|
version "4.3.7"
|
||||||
resolved "https://registry.yarnpkg.com/chai/-/chai-4.3.7.tgz#ec63f6df01829088e8bf55fca839bcd464a8ec51"
|
resolved "https://registry.yarnpkg.com/chai/-/chai-4.3.7.tgz#ec63f6df01829088e8bf55fca839bcd464a8ec51"
|
||||||
@ -4241,7 +4236,7 @@ get-caller-file@^2.0.1, get-caller-file@^2.0.5:
|
|||||||
resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e"
|
resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e"
|
||||||
integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==
|
integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==
|
||||||
|
|
||||||
get-func-name@2.0.2, get-func-name@^2.0.0, get-func-name@^2.0.2:
|
get-func-name@^2.0.0, get-func-name@^2.0.2:
|
||||||
version "2.0.2"
|
version "2.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.2.tgz#0d7cf20cd13fda808669ffa88f4ffc7a3943fc41"
|
resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.2.tgz#0d7cf20cd13fda808669ffa88f4ffc7a3943fc41"
|
||||||
integrity sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==
|
integrity sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==
|
||||||
@ -5760,9 +5755,9 @@ nanoid@3.3.1:
|
|||||||
integrity sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw==
|
integrity sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw==
|
||||||
|
|
||||||
nanoid@^3.3.6:
|
nanoid@^3.3.6:
|
||||||
version "3.3.7"
|
version "3.3.6"
|
||||||
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.7.tgz#d0c301a691bc8d54efa0a2226ccf3fe2fd656bd8"
|
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.6.tgz#443380c856d6e9f9824267d960b4236ad583ea4c"
|
||||||
integrity sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==
|
integrity sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==
|
||||||
|
|
||||||
natural-compare@^1.4.0:
|
natural-compare@^1.4.0:
|
||||||
version "1.4.0"
|
version "1.4.0"
|
||||||
|
Loading…
Reference in New Issue
Block a user