mx-puppet-bridge
Advanced tools
Comparing version 0.1.3 to 0.1.4
@@ -5,2 +5,3 @@ export declare class Config { | ||
database: DatabaseConfig; | ||
metrics: MetricsConfig; | ||
provisioning: ProvisioningConfig; | ||
@@ -55,2 +56,7 @@ presence: PresenceConfig; | ||
} | ||
export declare class MetricsConfig { | ||
enabled: boolean; | ||
port: number; | ||
path: string; | ||
} | ||
export declare class DatabaseConfig { | ||
@@ -57,0 +63,0 @@ connString: string; |
@@ -22,2 +22,3 @@ "use strict"; | ||
this.database = new DatabaseConfig(); | ||
this.metrics = new MetricsConfig(); | ||
this.provisioning = new ProvisioningConfig(); | ||
@@ -83,2 +84,10 @@ this.presence = new PresenceConfig(); | ||
exports.LoggingFileConfig = LoggingFileConfig; | ||
class MetricsConfig { | ||
constructor() { | ||
this.enabled = false; | ||
this.port = 8000; | ||
this.path = "/metrics"; | ||
} | ||
} | ||
exports.MetricsConfig = MetricsConfig; | ||
class DatabaseConfig { | ||
@@ -85,0 +94,0 @@ constructor() { |
@@ -0,1 +1,2 @@ | ||
import * as prometheus from "prom-client"; | ||
declare type SQLTYPES = number | boolean | string | null; | ||
@@ -10,2 +11,3 @@ export interface ISqlCommandParameters { | ||
type: string; | ||
latency: prometheus.Histogram<string>; | ||
Open(): void; | ||
@@ -12,0 +14,0 @@ Get(sql: string, parameters?: ISqlCommandParameters): Promise<ISqlRow | null>; |
@@ -5,3 +5,4 @@ import { IDatabaseConnector } from "./connector"; | ||
private db; | ||
constructor(db: IDatabaseConnector); | ||
private protocol; | ||
constructor(db: IDatabaseConnector, protocol?: string); | ||
newData(puppetId: number, roomId: string | null, emoteId: string): IEmoteStoreEntry; | ||
@@ -13,2 +14,3 @@ get(puppetId: number, roomId: string | null, emoteId: string): Promise<IEmoteStoreEntry | null>; | ||
private getFromRow; | ||
private labels; | ||
} |
@@ -27,4 +27,5 @@ "use strict"; | ||
class DbEmoteStore { | ||
constructor(db) { | ||
constructor(db, protocol = "unknown") { | ||
this.db = db; | ||
this.protocol = protocol; | ||
} | ||
@@ -45,2 +46,3 @@ newData(puppetId, roomId, emoteId) { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
const stopTimer = this.db.latency.startTimer(this.labels("select")); | ||
if (roomId) { | ||
@@ -52,2 +54,3 @@ const row = yield this.db.Get("SELECT * FROM emote_store WHERE puppet_id = $pid AND room_id = $rid AND emote_id = $eid LIMIT 1", { | ||
}); | ||
stopTimer(); | ||
return this.getFromRow(row); | ||
@@ -60,2 +63,3 @@ } | ||
}); | ||
stopTimer(); | ||
return this.getFromRow(row); | ||
@@ -67,2 +71,3 @@ } | ||
return __awaiter(this, void 0, void 0, function* () { | ||
const stopTimer = this.db.latency.startTimer(this.labels("select_by_mxc")); | ||
if (roomId) { | ||
@@ -74,2 +79,3 @@ const row = yield this.db.Get("SELECT * FROM emote_store WHERE puppet_id = $pid AND room_id = $rid AND avatar_mxc = $mxid LIMIT 1", { | ||
}); | ||
stopTimer(); | ||
return this.getFromRow(row); | ||
@@ -82,2 +88,3 @@ } | ||
}); | ||
stopTimer(); | ||
return this.getFromRow(row); | ||
@@ -89,2 +96,3 @@ } | ||
return __awaiter(this, void 0, void 0, function* () { | ||
const stopTimer = this.db.latency.startTimer(this.labels("select_by_room")); | ||
const rows = yield this.db.All("SELECT * FROM emote_store WHERE puppet_id = $pid AND room_id = $rid", { | ||
@@ -101,2 +109,3 @@ pid: puppetId, | ||
} | ||
stopTimer(); | ||
return result; | ||
@@ -107,2 +116,3 @@ }); | ||
return __awaiter(this, void 0, void 0, function* () { | ||
const stopTimer = this.db.latency.startTimer(this.labels("insert_update")); | ||
let exists = null; | ||
@@ -165,2 +175,3 @@ if (data.roomId) { | ||
}); | ||
stopTimer(); | ||
}); | ||
@@ -183,3 +194,11 @@ } | ||
} | ||
labels(queryName) { | ||
return { | ||
protocol: this.protocol, | ||
engine: this.db.type, | ||
table: "emote_store", | ||
type: queryName, | ||
}; | ||
} | ||
} | ||
exports.DbEmoteStore = DbEmoteStore; |
import { IDatabaseConnector } from "./connector"; | ||
export declare class DbEventStore { | ||
private db; | ||
constructor(db: IDatabaseConnector); | ||
private protocol; | ||
constructor(db: IDatabaseConnector, protocol?: string); | ||
insert(puppetId: number, roomId: string, matrixId: string, remoteId: string): Promise<void>; | ||
@@ -9,2 +10,3 @@ remove(puppetId: number, roomId: string, remoteId: string): Promise<void>; | ||
getRemote(puppetId: number, roomId: string, matrixId: string): Promise<string[]>; | ||
private labels; | ||
} |
@@ -27,7 +27,9 @@ "use strict"; | ||
class DbEventStore { | ||
constructor(db) { | ||
constructor(db, protocol = "unknown") { | ||
this.db = db; | ||
this.protocol = protocol; | ||
} | ||
insert(puppetId, roomId, matrixId, remoteId) { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
const stopTimer = this.db.latency.startTimer(this.labels("insert")); | ||
yield this.db.Run("INSERT INTO event_store (puppet_id, room_id, matrix_id, remote_id) VALUES ($p, $room, $m, $r)", { | ||
@@ -39,2 +41,3 @@ p: puppetId, | ||
}); | ||
stopTimer(); | ||
}); | ||
@@ -44,2 +47,3 @@ } | ||
return __awaiter(this, void 0, void 0, function* () { | ||
const stopTimer = this.db.latency.startTimer(this.labels("remove")); | ||
yield this.db.Run("DELETE FROM event_store WHERE puppet_id = $p AND room_id = $room AND remote_id = $r", { | ||
@@ -50,2 +54,3 @@ p: puppetId, | ||
}); | ||
stopTimer(); | ||
}); | ||
@@ -55,2 +60,3 @@ } | ||
return __awaiter(this, void 0, void 0, function* () { | ||
const stopTimer = this.db.latency.startTimer(this.labels("select_matrix")); | ||
const result = []; | ||
@@ -65,2 +71,3 @@ const rows = yield this.db.All("SELECT * FROM event_store WHERE puppet_id=$p AND room_id = $room AND remote_id=$r", { | ||
} | ||
stopTimer(); | ||
return result; | ||
@@ -71,2 +78,3 @@ }); | ||
return __awaiter(this, void 0, void 0, function* () { | ||
const stopTimer = this.db.latency.startTimer(this.labels("select_remote")); | ||
const result = []; | ||
@@ -81,6 +89,15 @@ const rows = yield this.db.All("SELECT * FROM event_store WHERE puppet_id = $p AND room_id = $room AND matrix_id = $m", { | ||
} | ||
stopTimer(); | ||
return result; | ||
}); | ||
} | ||
labels(queryName) { | ||
return { | ||
protocol: this.protocol, | ||
engine: this.db.type, | ||
table: "event_store", | ||
type: queryName, | ||
}; | ||
} | ||
} | ||
exports.DbEventStore = DbEventStore; |
@@ -6,3 +6,4 @@ import { IDatabaseConnector } from "./connector"; | ||
private groupsCache; | ||
constructor(db: IDatabaseConnector, cache?: boolean); | ||
private protocol; | ||
constructor(db: IDatabaseConnector, cache?: boolean, protocolId?: string); | ||
newData(mxid: string, groupId: string, puppetId: number): IGroupStoreEntry; | ||
@@ -15,2 +16,3 @@ getByRemote(puppetId: number, groupId: string, ignoreCache?: boolean): Promise<IGroupStoreEntry | null>; | ||
private getFromRow; | ||
private labels; | ||
} |
@@ -30,5 +30,6 @@ "use strict"; | ||
class DbGroupStore { | ||
constructor(db, cache = true) { | ||
constructor(db, cache = true, protocolId = "unknown") { | ||
this.db = db; | ||
this.groupsCache = new timedcache_1.TimedCache(cache ? GROUP_CACHE_LIFETIME : 0); | ||
this.protocol = protocolId; | ||
} | ||
@@ -45,2 +46,3 @@ newData(mxid, groupId, puppetId) { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
const stopTimer = this.db.latency.startTimer(this.labels("select_by_remote")); | ||
if (!ignoreCache) { | ||
@@ -56,3 +58,5 @@ const cached = this.groupsCache.get(`${puppetId};${groupId}`); | ||
}); | ||
return yield this.getFromRow(row); | ||
const result = yield this.getFromRow(row); | ||
stopTimer(); | ||
return result; | ||
}); | ||
@@ -62,2 +66,3 @@ } | ||
return __awaiter(this, void 0, void 0, function* () { | ||
const stopTimer = this.db.latency.startTimer(this.labels("select_by_puppet")); | ||
const rows = yield this.db.All("SELECT * FROM group_store WHERE puppet_id = $puppetId", { | ||
@@ -73,2 +78,3 @@ puppetId, | ||
} | ||
stopTimer(); | ||
return results; | ||
@@ -79,4 +85,7 @@ }); | ||
return __awaiter(this, void 0, void 0, function* () { | ||
const stopTimer = this.db.latency.startTimer(this.labels("select_by_mxid")); | ||
const row = yield this.db.Get("SELECT * FROM group_store WHERE mxid = $mxid", { mxid }); | ||
return yield this.getFromRow(row); | ||
const result = yield this.getFromRow(row); | ||
stopTimer(); | ||
return result; | ||
}); | ||
@@ -86,2 +95,3 @@ } | ||
return __awaiter(this, void 0, void 0, function* () { | ||
const stopTimer = this.db.latency.startTimer(this.labels("insert_update")); | ||
// first de-dupe the room IDs | ||
@@ -200,2 +210,3 @@ const uniqueRoomIds = []; | ||
this.groupsCache.set(`${data.puppetId};${data.groupId}`, data); | ||
stopTimer(); | ||
}); | ||
@@ -205,2 +216,3 @@ } | ||
return __awaiter(this, void 0, void 0, function* () { | ||
const stopTimer = this.db.latency.startTimer(this.labels("delete")); | ||
yield this.db.Run("DELETE FROM group_store WHERE mxid = $mxid", { mxid: data.mxid }); | ||
@@ -212,2 +224,3 @@ yield this.db.Run("DELETE FROM group_store_rooms WHERE puppet_id = $puppetId AND group_id = $groupId", { | ||
this.groupsCache.delete(`${data.puppetId};${data.groupId}`); | ||
stopTimer(); | ||
}); | ||
@@ -217,2 +230,3 @@ } | ||
return __awaiter(this, void 0, void 0, function* () { | ||
const stopTimer = this.db.latency.startTimer(this.labels("select_from_row")); | ||
if (!row) { | ||
@@ -238,6 +252,15 @@ return null; | ||
this.groupsCache.set(`${data.puppetId};${data.groupId}`, data); | ||
stopTimer(); | ||
return data; | ||
}); | ||
} | ||
labels(queryName) { | ||
return { | ||
protocol: this.protocol, | ||
engine: this.db.type, | ||
table: "group_store", | ||
type: queryName, | ||
}; | ||
} | ||
} | ||
exports.DbGroupStore = DbGroupStore; |
import { IDatabaseConnector, ISqlCommandParameters, ISqlRow } from "./connector"; | ||
import * as prometheus from "prom-client"; | ||
export declare class Postgres implements IDatabaseConnector { | ||
@@ -6,2 +7,3 @@ private connectionString; | ||
type: string; | ||
latency: prometheus.Histogram<string>; | ||
private db; | ||
@@ -8,0 +10,0 @@ constructor(connectionString: string); |
@@ -29,2 +29,3 @@ "use strict"; | ||
const log_1 = require("../log"); | ||
const prometheus = require("prom-client"); | ||
const log = new log_1.Log("Postgres"); | ||
@@ -38,2 +39,9 @@ const pgp = pgPromise({ | ||
this.type = "postgres"; | ||
this.latency = new prometheus.Histogram({ | ||
name: "bridge_database_query_seconds", | ||
help: "Time spent querying the database engine", | ||
labelNames: ["protocol", "engine", "type", "table"], | ||
// tslint:disable-next-line no-magic-numbers | ||
buckets: [0.002, 0.005, 0.0075, 0.01, 0.02, 0.05, 0.1, 0.2, 0.5, 1, 2, 5], | ||
}); | ||
} | ||
@@ -40,0 +48,0 @@ static ParameterizeSql(sql) { |
@@ -29,3 +29,4 @@ import { IDatabaseConnector } from "./connector"; | ||
private allPuppetIds; | ||
constructor(db: IDatabaseConnector, cache?: boolean); | ||
private protocol; | ||
constructor(db: IDatabaseConnector, cache?: boolean, protocol?: string); | ||
deleteStatusRoom(mxid: string): Promise<void>; | ||
@@ -54,2 +55,3 @@ getMxidInfo(puppetMxid: string): Promise<IMxidInfo | null>; | ||
private getRow; | ||
private labels; | ||
} |
@@ -33,3 +33,3 @@ "use strict"; | ||
class DbPuppetStore { | ||
constructor(db, cache = true) { | ||
constructor(db, cache = true, protocol = "unknown") { | ||
this.db = db; | ||
@@ -40,6 +40,9 @@ this.mxidCache = new timedcache_1.TimedCache(cache ? PUPPET_CACHE_LIFETIME : 0); | ||
this.allPuppetIds = null; | ||
this.protocol = protocol; | ||
} | ||
deleteStatusRoom(mxid) { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
const stopTimer = this.db.latency.startTimer(this.labels("update_status")); | ||
yield this.db.Run("UPDATE puppet_mxid_store SET status_room = '' WHERE status_room = $mxid", { mxid }); | ||
stopTimer(); | ||
}); | ||
@@ -49,2 +52,3 @@ } | ||
return __awaiter(this, void 0, void 0, function* () { | ||
const stopTimer = this.db.latency.startTimer(this.labels("get_mx_info")); | ||
const row = yield this.db.Get("SELECT * FROM puppet_mxid_store WHERE puppet_mxid=$id", { id: puppetMxid }); | ||
@@ -54,2 +58,3 @@ if (!row) { | ||
} | ||
stopTimer(); | ||
return { | ||
@@ -89,2 +94,3 @@ puppetMxid, | ||
return __awaiter(this, void 0, void 0, function* () { | ||
const stopTimer = this.db.latency.startTimer(this.labels("set_mxid_info")); | ||
const exists = yield this.db.Get("SELECT * FROM puppet_mxid_store WHERE puppet_mxid=$id", { id: puppet.puppetMxid }); | ||
@@ -122,2 +128,3 @@ let query = ""; | ||
}); | ||
stopTimer(); | ||
}); | ||
@@ -127,2 +134,3 @@ } | ||
return __awaiter(this, void 0, void 0, function* () { | ||
const stopTimer = this.db.latency.startTimer(this.labels("select_all")); | ||
let result = []; | ||
@@ -153,2 +161,3 @@ if (this.allPuppetIds) { | ||
} | ||
stopTimer(); | ||
return result; | ||
@@ -159,2 +168,3 @@ }); | ||
return __awaiter(this, void 0, void 0, function* () { | ||
const stopTimer = this.db.latency.startTimer(this.labels("select_for_mx")); | ||
const result = []; | ||
@@ -168,2 +178,3 @@ const rows = yield this.db.All("SELECT * FROM puppet_store WHERE puppet_mxid=$mxid", { mxid: puppetMxid }); | ||
} | ||
stopTimer(); | ||
return result; | ||
@@ -174,2 +185,3 @@ }); | ||
return __awaiter(this, void 0, void 0, function* () { | ||
const stopTimer = this.db.latency.startTimer(this.labels("select")); | ||
const cached = this.puppetCache.get(puppetId); | ||
@@ -183,2 +195,3 @@ if (cached) { | ||
} | ||
stopTimer(); | ||
return this.getRow(row); | ||
@@ -189,2 +202,3 @@ }); | ||
return __awaiter(this, void 0, void 0, function* () { | ||
const stopTimer = this.db.latency.startTimer(this.labels("select_mxid")); | ||
const cached = this.mxidCache.get(puppetId); | ||
@@ -200,2 +214,3 @@ if (cached) { | ||
this.mxidCache.set(puppetId, mxid); | ||
stopTimer(); | ||
return mxid; | ||
@@ -206,2 +221,3 @@ }); | ||
return __awaiter(this, void 0, void 0, function* () { | ||
const stopTimer = this.db.latency.startTimer(this.labels("update_uid")); | ||
yield this.db.Run("UPDATE puppet_store SET user_id=$uid WHERE puppet_id=$pid", { | ||
@@ -212,2 +228,3 @@ uid: userId, | ||
this.puppetCache.delete(puppetId); | ||
stopTimer(); | ||
}); | ||
@@ -217,2 +234,3 @@ } | ||
return __awaiter(this, void 0, void 0, function* () { | ||
const stopTimer = this.db.latency.startTimer(this.labels("update_data")); | ||
let dataStr = ""; | ||
@@ -231,2 +249,3 @@ try { | ||
this.puppetCache.delete(puppetId); | ||
stopTimer(); | ||
}); | ||
@@ -236,2 +255,3 @@ } | ||
return __awaiter(this, void 0, void 0, function* () { | ||
const stopTimer = this.db.latency.startTimer(this.labels("update_type")); | ||
yield this.db.Run("UPDATE puppet_store SET type=$t WHERE puppet_id=$id", { | ||
@@ -242,2 +262,3 @@ id: puppetId, | ||
this.puppetCache.delete(puppetId); | ||
stopTimer(); | ||
}); | ||
@@ -247,2 +268,3 @@ } | ||
return __awaiter(this, void 0, void 0, function* () { | ||
const stopTimer = this.db.latency.startTimer(this.labels("update_visibility")); | ||
yield this.db.Run("UPDATE puppet_store SET is_public=$p WHERE puppet_id=$id", { | ||
@@ -253,2 +275,3 @@ id: puppetId, | ||
this.puppetCache.delete(puppetId); | ||
stopTimer(); | ||
}); | ||
@@ -258,2 +281,3 @@ } | ||
return __awaiter(this, void 0, void 0, function* () { | ||
const stopTimer = this.db.latency.startTimer(this.labels("update_autoinvite")); | ||
yield this.db.Run("UPDATE puppet_store SET autoinvite=$a WHERE puppet_id=$id", { | ||
@@ -264,2 +288,3 @@ id: puppetId, | ||
this.puppetCache.delete(puppetId); | ||
stopTimer(); | ||
}); | ||
@@ -269,2 +294,3 @@ } | ||
return __awaiter(this, void 0, void 0, function* () { | ||
const stopTimer = this.db.latency.startTimer(this.labels("update_namespace")); | ||
yield this.db.Run("UPDATE puppet_store SET is_global_namespace=$is WHERE puppet_id=$id", { | ||
@@ -275,2 +301,3 @@ id: puppetId, | ||
this.puppetCache.delete(puppetId); | ||
stopTimer(); | ||
}); | ||
@@ -280,2 +307,3 @@ } | ||
return __awaiter(this, void 0, void 0, function* () { | ||
const stopTimer = this.db.latency.startTimer(this.labels("insert")); | ||
let dataStr = ""; | ||
@@ -300,2 +328,3 @@ try { | ||
this.allPuppetIds = null; | ||
stopTimer(); | ||
return puppetId; | ||
@@ -306,2 +335,3 @@ }); | ||
return __awaiter(this, void 0, void 0, function* () { | ||
const stopTimer = this.db.latency.startTimer(this.labels("delete")); | ||
yield this.db.Run("DELETE FROM puppet_store WHERE puppet_id=$id", { id: puppetId }); | ||
@@ -311,2 +341,3 @@ this.mxidCache.delete(puppetId); | ||
this.allPuppetIds = null; | ||
stopTimer(); | ||
}); | ||
@@ -316,2 +347,3 @@ } | ||
return __awaiter(this, void 0, void 0, function* () { | ||
const stopTimer = this.db.latency.startTimer(this.labels("select_ghost_in_room")); | ||
const exists = yield this.db.Get("SELECT * FROM ghosts_joined_chans WHERE ghost_mxid = $ghostMxid AND chan_mxid = $roomMxid", { | ||
@@ -321,2 +353,3 @@ ghostMxid, | ||
}); | ||
stopTimer(); | ||
return exists ? true : false; | ||
@@ -327,2 +360,3 @@ }); | ||
return __awaiter(this, void 0, void 0, function* () { | ||
const stopTimer = this.db.latency.startTimer(this.labels("insert_ghost_in_room")); | ||
if (yield this.isGhostInRoom(ghostMxid, roomMxid)) { | ||
@@ -335,2 +369,3 @@ return; | ||
}); | ||
stopTimer(); | ||
}); | ||
@@ -340,2 +375,3 @@ } | ||
return __awaiter(this, void 0, void 0, function* () { | ||
const stopTimer = this.db.latency.startTimer(this.labels("select_all_ghost_in_room")); | ||
const result = []; | ||
@@ -346,2 +382,3 @@ const rows = yield this.db.All("SELECT * FROM ghosts_joined_chans WHERE chan_mxid = $room", { room }); | ||
} | ||
stopTimer(); | ||
return result; | ||
@@ -352,2 +389,3 @@ }); | ||
return __awaiter(this, void 0, void 0, function* () { | ||
const stopTimer = this.db.latency.startTimer(this.labels("select_all_rooms_of_ghost")); | ||
const result = []; | ||
@@ -358,2 +396,3 @@ const rows = yield this.db.All("SELECT * FROM ghosts_joined_chans WHERE ghost_mxid = $ghost", { ghost }); | ||
} | ||
stopTimer(); | ||
return result; | ||
@@ -364,3 +403,5 @@ }); | ||
return __awaiter(this, void 0, void 0, function* () { | ||
const stopTimer = this.db.latency.startTimer(this.labels("delete_ghosts_in_room")); | ||
yield this.db.Run("DELETE FROM ghosts_joined_chans WHERE chan_mxid = $room", { room }); | ||
stopTimer(); | ||
}); | ||
@@ -370,2 +411,3 @@ } | ||
return __awaiter(this, void 0, void 0, function* () { | ||
const stopTimer = this.db.latency.startTimer(this.labels("delete_ghost_in_room")); | ||
yield this.db.Run("DELETE FROM ghosts_joined_chans " + | ||
@@ -376,2 +418,3 @@ "WHERE ghost_mxid = $g AND chan_mxid = $c", { | ||
}); | ||
stopTimer(); | ||
}); | ||
@@ -399,3 +442,11 @@ } | ||
} | ||
labels(queryName) { | ||
return { | ||
protocol: this.protocol, | ||
engine: this.db.type, | ||
table: "puppet_store", | ||
type: queryName, | ||
}; | ||
} | ||
} | ||
exports.DbPuppetStore = DbPuppetStore; |
@@ -12,3 +12,4 @@ import { IDatabaseConnector } from "./connector"; | ||
private db; | ||
constructor(db: IDatabaseConnector); | ||
private protocol; | ||
constructor(db: IDatabaseConnector, protocol?: string); | ||
exists(data: IReactionStoreEntry): Promise<boolean>; | ||
@@ -22,2 +23,3 @@ insert(data: IReactionStoreEntry): Promise<boolean>; | ||
private getFromRow; | ||
private labels; | ||
} |
@@ -27,7 +27,9 @@ "use strict"; | ||
class DbReactionStore { | ||
constructor(db) { | ||
constructor(db, protocol = "unknown") { | ||
this.db = db; | ||
this.protocol = protocol; | ||
} | ||
exists(data) { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
const stopTimer = this.db.latency.startTimer(this.labels("select_exists")); | ||
const exists = yield this.db.Get(`SELECT 1 FROM reaction_store WHERE puppet_id = $pid AND user_id = $uid | ||
@@ -41,2 +43,3 @@ AND room_id = $rid AND user_id = $uid AND event_id = $eid AND key = $key`, { | ||
}); | ||
stopTimer(); | ||
return exists ? true : false; | ||
@@ -47,2 +50,3 @@ }); | ||
return __awaiter(this, void 0, void 0, function* () { | ||
const stopTimer = this.db.latency.startTimer(this.labels("insert")); | ||
if (yield this.exists(data)) { | ||
@@ -61,2 +65,3 @@ return false; | ||
}); | ||
stopTimer(); | ||
return true; | ||
@@ -67,3 +72,5 @@ }); | ||
return __awaiter(this, void 0, void 0, function* () { | ||
const stopTimer = this.db.latency.startTimer(this.labels("select_by_reaction_mxid")); | ||
const row = yield this.db.Get("SELECT * FROM reaction_store WHERE reaction_mxid = $reactionMxid", { reactionMxid }); | ||
stopTimer(); | ||
return this.getFromRow(row); | ||
@@ -74,2 +81,3 @@ }); | ||
return __awaiter(this, void 0, void 0, function* () { | ||
const stopTimer = this.db.latency.startTimer(this.labels("select_by_key")); | ||
const row = yield this.db.Get(`SELECT * FROM reaction_store WHERE puppet_id = $pid AND user_id = $uid AND room_id = $rid | ||
@@ -83,2 +91,3 @@ AND event_id = $eid AND key = $key`, { | ||
}); | ||
stopTimer(); | ||
return this.getFromRow(row); | ||
@@ -89,2 +98,3 @@ }); | ||
return __awaiter(this, void 0, void 0, function* () { | ||
const stopTimer = this.db.latency.startTimer(this.labels("select_for_event")); | ||
const rows = yield this.db.All("SELECT * FROM reaction_store WHERE puppet_id = $puppetId AND event_id = $eventId", { puppetId, eventId }); | ||
@@ -98,2 +108,3 @@ const result = []; | ||
} | ||
stopTimer(); | ||
return result; | ||
@@ -104,3 +115,5 @@ }); | ||
return __awaiter(this, void 0, void 0, function* () { | ||
const stopTimer = this.db.latency.startTimer(this.labels("delete")); | ||
yield this.db.Run("DELETE FROM reaction_store WHERE reaction_mxid = $reactionMxid", { reactionMxid }); | ||
stopTimer(); | ||
}); | ||
@@ -110,3 +123,5 @@ } | ||
return __awaiter(this, void 0, void 0, function* () { | ||
const stopTimer = this.db.latency.startTimer(this.labels("delete_for_event")); | ||
yield this.db.Run("DELETE FROM reaction_store WHERE puppet_id = $puppetId AND event_id = $eventId", { puppetId, eventId }); | ||
stopTimer(); | ||
}); | ||
@@ -127,3 +142,11 @@ } | ||
} | ||
labels(queryName) { | ||
return { | ||
protocol: this.protocol, | ||
engine: this.db.type, | ||
table: "reaction_store", | ||
type: queryName, | ||
}; | ||
} | ||
} | ||
exports.DbReactionStore = DbReactionStore; |
@@ -8,3 +8,4 @@ import { IDatabaseConnector } from "./connector"; | ||
private opCache; | ||
constructor(db: IDatabaseConnector, cache?: boolean); | ||
private protocol; | ||
constructor(db: IDatabaseConnector, cache?: boolean, protocol?: string); | ||
newData(mxid: string, roomId: string, puppetId: number): IRoomStoreEntry; | ||
@@ -21,2 +22,3 @@ getAll(): Promise<IRoomStoreEntry[]>; | ||
private getFromRow; | ||
private labels; | ||
} |
@@ -30,3 +30,3 @@ "use strict"; | ||
class DbRoomStore { | ||
constructor(db, cache = true) { | ||
constructor(db, cache = true, protocol = "unknown") { | ||
this.db = db; | ||
@@ -36,2 +36,3 @@ this.remoteCache = new timedcache_1.TimedCache(cache ? ROOM_CACHE_LIFETIME : 0); | ||
this.opCache = new timedcache_1.TimedCache(cache ? ROOM_CACHE_LIFETIME : 0); | ||
this.protocol = protocol; | ||
} | ||
@@ -50,2 +51,3 @@ newData(mxid, roomId, puppetId) { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
const stopTimer = this.db.latency.startTimer(this.labels("select_all")); | ||
const rows = yield this.db.All("SELECT * FROM room_store"); | ||
@@ -59,2 +61,3 @@ const results = []; | ||
} | ||
stopTimer(); | ||
return results; | ||
@@ -65,2 +68,3 @@ }); | ||
return __awaiter(this, void 0, void 0, function* () { | ||
const stopTimer = this.db.latency.startTimer(this.labels("select_by_remote")); | ||
const cached = this.remoteCache.get(`${puppetId};${roomId}`); | ||
@@ -74,2 +78,3 @@ if (cached) { | ||
}); | ||
stopTimer(); | ||
return this.getFromRow(row); | ||
@@ -80,2 +85,3 @@ }); | ||
return __awaiter(this, void 0, void 0, function* () { | ||
const stopTimer = this.db.latency.startTimer(this.labels("select_by_puppet")); | ||
const rows = yield this.db.All("SELECT * FROM room_store WHERE puppet_id = $puppet_id", { | ||
@@ -91,2 +97,3 @@ puppet_id: puppetId, | ||
} | ||
stopTimer(); | ||
return results; | ||
@@ -97,2 +104,3 @@ }); | ||
return __awaiter(this, void 0, void 0, function* () { | ||
const stopTimer = this.db.latency.startTimer(this.labels("select_by_mxid")); | ||
const cached = this.mxidCache.get(mxid); | ||
@@ -103,2 +111,3 @@ if (cached) { | ||
const row = yield this.db.Get("SELECT * FROM room_store WHERE mxid = $mxid", { mxid }); | ||
stopTimer(); | ||
return this.getFromRow(row); | ||
@@ -109,2 +118,3 @@ }); | ||
return __awaiter(this, void 0, void 0, function* () { | ||
const stopTimer = this.db.latency.startTimer(this.labels("insert_update")); | ||
const exists = yield this.db.Get("SELECT * FROM room_store WHERE mxid = $mxid", { mxid: data.mxid }); | ||
@@ -176,2 +186,3 @@ let query = ""; | ||
this.mxidCache.set(data.mxid, data); | ||
stopTimer(); | ||
}); | ||
@@ -181,2 +192,3 @@ } | ||
return __awaiter(this, void 0, void 0, function* () { | ||
const stopTimer = this.db.latency.startTimer(this.labels("delete")); | ||
yield this.db.Run("DELETE FROM room_store WHERE mxid = $mxid", { mxid: data.mxid }); | ||
@@ -187,2 +199,3 @@ yield this.db.Run("DELETE FROM chan_op WHERE chan_mxid=$mxid", { mxid: data.mxid }); | ||
this.opCache.delete(data.mxid); | ||
stopTimer(); | ||
}); | ||
@@ -192,2 +205,3 @@ } | ||
return __awaiter(this, void 0, void 0, function* () { | ||
const stopTimer = this.db.latency.startTimer(this.labels("update_namespace")); | ||
const exists = yield this.getByRemote(-1, roomId); | ||
@@ -208,2 +222,3 @@ if (exists) { | ||
this.opCache.delete(room.mxid); | ||
stopTimer(); | ||
}); | ||
@@ -213,2 +228,3 @@ } | ||
return __awaiter(this, void 0, void 0, function* () { | ||
const stopTimer = this.db.latency.startTimer(this.labels("update_room_op")); | ||
const row = yield this.db.Get("SELECT * FROM chan_op WHERE chan_mxid=$chan LIMIT 1", { | ||
@@ -220,2 +236,3 @@ chan: roomMxid, | ||
// nothing to do, we are already set | ||
stopTimer(); | ||
return; | ||
@@ -232,2 +249,3 @@ } | ||
this.opCache.set(roomMxid, userMxid); | ||
stopTimer(); | ||
}); | ||
@@ -237,2 +255,3 @@ } | ||
return __awaiter(this, void 0, void 0, function* () { | ||
const stopTimer = this.db.latency.startTimer(this.labels("select_room_op")); | ||
const cached = this.opCache.get(roomMxid); | ||
@@ -250,2 +269,3 @@ if (cached) { | ||
this.opCache.set(roomMxid, userMxid); | ||
stopTimer(); | ||
return userMxid; | ||
@@ -273,3 +293,11 @@ }); | ||
} | ||
labels(queryName) { | ||
return { | ||
protocol: this.protocol, | ||
engine: this.db.type, | ||
table: "room_store", | ||
type: queryName, | ||
}; | ||
} | ||
} | ||
exports.DbRoomStore = DbRoomStore; |
import { IDatabaseConnector, ISqlCommandParameters, ISqlRow } from "./connector"; | ||
import * as prometheus from "prom-client"; | ||
export declare class SQLite3 implements IDatabaseConnector { | ||
private filename; | ||
type: string; | ||
latency: prometheus.Histogram<string>; | ||
private db; | ||
@@ -6,0 +8,0 @@ private insertId; |
@@ -29,2 +29,3 @@ "use strict"; | ||
const log_1 = require("../log"); | ||
const prometheus = require("prom-client"); | ||
const log = new log_1.Log("SQLite3"); | ||
@@ -36,2 +37,9 @@ class SQLite3 { | ||
this.insertId = -1; | ||
this.latency = new prometheus.Histogram({ | ||
name: "bridge_database_query_seconds", | ||
help: "Time spent querying the database engine", | ||
labelNames: ["protocol", "engine", "type", "table"], | ||
// tslint:disable-next-line no-magic-numbers | ||
buckets: [0.002, 0.005, 0.0075, 0.01, 0.02, 0.05, 0.1, 0.2, 0.5, 1, 2, 5], | ||
}); | ||
} | ||
@@ -38,0 +46,0 @@ Open() { |
@@ -6,4 +6,6 @@ import { IDatabaseConnector } from "./connector"; | ||
private usersCache; | ||
constructor(db: IDatabaseConnector, cache?: boolean); | ||
private protocol; | ||
constructor(db: IDatabaseConnector, cache?: boolean, protocol?: string); | ||
newData(puppetId: number, userId: string): IUserStoreEntry; | ||
getAll(): Promise<IUserStoreEntry[]>; | ||
get(puppetId: number, userId: string): Promise<IUserStoreEntry | null>; | ||
@@ -17,2 +19,3 @@ set(data: IUserStoreEntry): Promise<void>; | ||
private getRoomOverrideFromRow; | ||
private labels; | ||
} |
@@ -30,5 +30,6 @@ "use strict"; | ||
class DbUserStore { | ||
constructor(db, cache = true) { | ||
constructor(db, cache = true, protocol = "unknown") { | ||
this.db = db; | ||
this.usersCache = new timedcache_1.TimedCache(cache ? USERS_CACHE_LIFETIME : 0); | ||
this.protocol = protocol; | ||
} | ||
@@ -41,4 +42,28 @@ newData(puppetId, userId) { | ||
} | ||
getAll() { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
const stopTimer = this.db.latency.startTimer(this.labels("select_all")); | ||
const results = []; | ||
const rows = yield this.db.All("SELECT * FROM user_store;"); | ||
if (!rows) { | ||
return []; | ||
} | ||
for (const r of rows) { | ||
const data = { | ||
name: r.name, | ||
userId: r.user_id, | ||
puppetId: r.puppet_id, | ||
avatarUrl: r.avatar_url, | ||
avatarMxc: r.avatar_mxc, | ||
avatarHash: r.avatar_hash, | ||
}; | ||
results.push(data); | ||
} | ||
stopTimer(); | ||
return results; | ||
}); | ||
} | ||
get(puppetId, userId) { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
const stopTimer = this.db.latency.startTimer(this.labels("select")); | ||
const cacheKey = `${puppetId};${userId}`; | ||
@@ -59,2 +84,3 @@ const cached = this.usersCache.get(cacheKey); | ||
this.usersCache.set(cacheKey, data); | ||
stopTimer(); | ||
return data; | ||
@@ -65,2 +91,3 @@ }); | ||
return __awaiter(this, void 0, void 0, function* () { | ||
const stopTimer = this.db.latency.startTimer(this.labels("insert_update")); | ||
const exists = yield this.db.Get("SELECT 1 FROM user_store WHERE user_id = $id AND puppet_id = $pid", { id: data.userId, pid: data.puppetId }); | ||
@@ -103,2 +130,3 @@ let query = ""; | ||
this.usersCache.set(cacheKey, data); | ||
stopTimer(); | ||
}); | ||
@@ -108,2 +136,3 @@ } | ||
return __awaiter(this, void 0, void 0, function* () { | ||
const stopTimer = this.db.latency.startTimer(this.labels("delete")); | ||
yield this.db.Run("DELETE FROM user_store WHERE user_id = $user_id AND puppet_id = $puppet_id", { | ||
@@ -120,2 +149,3 @@ user_id: data.userId, | ||
this.usersCache.delete(cacheKey); | ||
stopTimer(); | ||
}); | ||
@@ -132,2 +162,3 @@ } | ||
return __awaiter(this, void 0, void 0, function* () { | ||
const stopTimer = this.db.latency.startTimer(this.labels("get_room_override")); | ||
const row = yield this.db.Get("SELECT * FROM user_store_room_override WHERE user_id = $uid AND puppet_id = $pid AND room_id = $rid", { | ||
@@ -141,2 +172,3 @@ uid: userId, | ||
} | ||
stopTimer(); | ||
return this.getRoomOverrideFromRow(row); | ||
@@ -147,2 +179,3 @@ }); | ||
return __awaiter(this, void 0, void 0, function* () { | ||
const stopTimer = this.db.latency.startTimer(this.labels("insert_update_room_override")); | ||
const exists = yield this.db.Get("SELECT 1 FROM user_store_room_override WHERE user_id = $uid AND puppet_id = $pid AND room_id = $rid", { | ||
@@ -190,2 +223,3 @@ uid: data.userId, | ||
}); | ||
stopTimer(); | ||
}); | ||
@@ -195,2 +229,3 @@ } | ||
return __awaiter(this, void 0, void 0, function* () { | ||
const stopTimer = this.db.latency.startTimer(this.labels("select_all_room_override")); | ||
const result = []; | ||
@@ -207,2 +242,3 @@ const rows = yield this.db.All("SELECT * FROM user_store_room_override WHERE user_id = $uid AND puppet_id = $pid", { | ||
} | ||
stopTimer(); | ||
return result; | ||
@@ -222,3 +258,11 @@ }); | ||
} | ||
labels(queryName) { | ||
return { | ||
protocol: this.protocol, | ||
engine: this.db.type, | ||
table: "user_store", | ||
type: queryName, | ||
}; | ||
} | ||
} | ||
exports.DbUserStore = DbUserStore; |
@@ -27,2 +27,3 @@ "use strict"; | ||
const escapeHtml = require("escape-html"); | ||
const prometheus = require("prom-client"); | ||
const log = new log_1.Log("MatrixEventHandler"); | ||
@@ -38,2 +39,19 @@ // tslint:disable no-magic-numbers | ||
this.memberInfoCache = {}; | ||
this.bridge.metrics.matrixEvent = new prometheus.Counter({ | ||
name: "bridge_matrix_events_total", | ||
help: "Total matrix events bridged to the remote network, by protocol and type", | ||
labelNames: ["protocol", "type"], | ||
}); | ||
this.bridge.metrics.matrixEventBucket = new prometheus.Histogram({ | ||
name: "bridge_matrix_event_seconds", | ||
help: "Time spent processing matrix events in seconds, by protocol", | ||
labelNames: ["protocol", "type"], | ||
// tslint:disable-next-line no-magic-numbers | ||
buckets: [0.002, 0.005, 0.01, 0.25, 0.5, 0.75, 1, 1.5, 2, 3, 5, 7, 10], | ||
}); | ||
this.bridge.metrics.matrixEventError = new prometheus.Counter({ | ||
name: "bridge_matrix_event_errors_total", | ||
help: "Errors encountered during matrix event processing", | ||
labelNames: ["protocol"], | ||
}); | ||
} | ||
@@ -43,2 +61,6 @@ registerAppserviceEvents() { | ||
this.bridge.AS.on("room.event", (roomId, rawEvent) => __awaiter(this, void 0, void 0, function* () { | ||
const stopTimer = this.bridge.metrics.matrixEventBucket.startTimer({ | ||
protocol: this.bridge.protocol.id, | ||
type: "room.event", | ||
}); | ||
try { | ||
@@ -49,6 +71,14 @@ yield this.handleRoomEvent(roomId, new matrix_bot_sdk_1.RoomEvent(rawEvent)); | ||
log.error("Error handling appservice room.event", err.error || err.body || err); | ||
this.bridge.metrics.matrixEventError.inc({ protocol: this.bridge.protocol.id }); | ||
} | ||
finally { | ||
stopTimer(); | ||
} | ||
})); | ||
// tslint:disable-next-line no-any | ||
this.bridge.AS.on("room.invite", (roomId, rawEvent) => __awaiter(this, void 0, void 0, function* () { | ||
const stopTimer = this.bridge.metrics.matrixEventBucket.startTimer({ | ||
protocol: this.bridge.protocol.id, | ||
type: "room.invite", | ||
}); | ||
try { | ||
@@ -59,6 +89,14 @@ yield this.handleInviteEvent(roomId, new matrix_bot_sdk_1.MembershipEvent(rawEvent)); | ||
log.error("Error handling appservice room.invite", err.error || err.body || err); | ||
this.bridge.metrics.matrixEventError.inc({ protocol: this.bridge.protocol.id }); | ||
} | ||
finally { | ||
stopTimer(); | ||
} | ||
})); | ||
// tslint:disable-next-line no-any | ||
this.bridge.AS.on("query.room", (alias, createRoom) => __awaiter(this, void 0, void 0, function* () { | ||
const stopTimer = this.bridge.metrics.matrixEventBucket.startTimer({ | ||
protocol: this.bridge.protocol.id, | ||
type: "query.room", | ||
}); | ||
try { | ||
@@ -68,7 +106,15 @@ yield this.handleRoomQuery(alias, createRoom); | ||
catch (err) { | ||
this.bridge.metrics.matrixEventError.inc({ protocol: this.bridge.protocol.id }); | ||
log.error("Error handling appservice query.room", err.error || err.body || err); | ||
} | ||
finally { | ||
stopTimer(); | ||
} | ||
})); | ||
// tslint:disable-next-line no-any | ||
this.bridge.AS.on("ephemeral.event", (rawEvent) => __awaiter(this, void 0, void 0, function* () { | ||
const stopTimer = this.bridge.metrics.matrixEventBucket.startTimer({ | ||
protocol: this.bridge.protocol.id, | ||
type: "ephemeral.event", | ||
}); | ||
try { | ||
@@ -90,2 +136,3 @@ switch (rawEvent.type) { | ||
} | ||
stopTimer(); | ||
})); | ||
@@ -136,2 +183,6 @@ } | ||
const membershipEvent = new matrix_bot_sdk_1.MembershipEvent(event.raw); | ||
this.bridge.metrics.matrixEvent.inc({ | ||
protocol: this.bridge.protocol.id, | ||
type: `${event.type}.${membershipEvent.membership}`, | ||
}); | ||
switch (membershipEvent.membership) { | ||
@@ -151,2 +202,6 @@ case "join": | ||
yield this.handleRedactEvent(roomId, evt); | ||
this.bridge.metrics.matrixEvent.inc({ | ||
protocol: this.bridge.protocol.id, | ||
type: event.type, | ||
}); | ||
return; | ||
@@ -379,2 +434,6 @@ } | ||
} | ||
this.bridge.metrics.matrixEvent.inc({ | ||
protocol: this.bridge.protocol.id, | ||
type: msgtype, | ||
}); | ||
}); | ||
@@ -514,2 +573,3 @@ } | ||
handleInviteEvent(roomId, invite) { | ||
var _a; | ||
return __awaiter(this, void 0, void 0, function* () { | ||
@@ -593,2 +653,3 @@ const userId = invite.membershipFor; | ||
yield this.bridge.userSync.getClient(parts); // create user, if it doesn't exist | ||
(_a = this.bridge.metrics.room) === null || _a === void 0 ? void 0 : _a.inc({ type: roomData.isDirect ? "dm" : "group", protocol: this.bridge.protocol.id }); | ||
}); | ||
@@ -595,0 +656,0 @@ } |
@@ -330,2 +330,3 @@ "use strict"; | ||
yield this.bridge.roomSync.rebridge(mxid, newRoomParts); | ||
this.bridge.metrics.room.inc({ type: "group", protocol: this.bridge.protocol.id }); | ||
}); | ||
@@ -348,2 +349,3 @@ } | ||
yield this.bridge.roomSync.delete(roomParts, true); | ||
this.bridge.metrics.room.dec({ type: "group", protocol: this.bridge.protocol.id }); | ||
return true; | ||
@@ -409,3 +411,6 @@ }); | ||
} | ||
else if (!(yield this.bridge.namespaceHandler.canSeeRoom(roomParts, userId))) { | ||
else if (!(yield this.bridge.namespaceHandler.canSeeRoom({ | ||
puppetId: yield this.bridge.namespaceHandler.getDbPuppetId(roomParts.puppetId), | ||
roomId: roomParts.roomId, | ||
}, userId))) { | ||
targetLevel = MUTED_POWER_LEVEL; | ||
@@ -481,3 +486,6 @@ } | ||
} | ||
if (yield this.bridge.namespaceHandler.canSeeRoom(room, userId)) { | ||
if (yield this.bridge.namespaceHandler.canSeeRoom({ | ||
roomId: room.roomId, | ||
puppetId: yield this.bridge.namespaceHandler.getDbPuppetId(room.puppetId), | ||
}, userId)) { | ||
const client = (yield this.bridge.roomSync.getRoomOp(room.mxid)) || this.bridge.botIntent.underlyingClient; | ||
@@ -484,0 +492,0 @@ try { |
/// <reference types="node" /> | ||
import { Appservice, Intent, MatrixClient } from "@sorunome/matrix-bot-sdk"; | ||
import * as prometheus from "prom-client"; | ||
import { EventEmitter } from "events"; | ||
@@ -55,2 +56,13 @@ import { EmoteSyncroniser } from "./emotesyncroniser"; | ||
} | ||
export declare class BridgeMetrics { | ||
room: prometheus.Gauge<string>; | ||
puppet: prometheus.Gauge<string>; | ||
message: prometheus.Counter<string>; | ||
remoteUser: prometheus.Gauge<string>; | ||
matrixEvent: prometheus.Counter<string>; | ||
matrixEventBucket: prometheus.Histogram<string>; | ||
matrixEventError: prometheus.Counter<string>; | ||
remoteUpdateBucket: prometheus.Histogram<string>; | ||
connected: prometheus.Gauge<string>; | ||
} | ||
export declare class PuppetBridge extends EventEmitter { | ||
@@ -76,2 +88,3 @@ private registrationPath; | ||
namespaceHandler: NamespaceHandler; | ||
metrics: BridgeMetrics; | ||
private appservice; | ||
@@ -81,2 +94,3 @@ private mxcLookupLock; | ||
private remoteEventHandler; | ||
private connectionMetricStatus; | ||
constructor(registrationPath: string, configPath: string, prot?: IProtocolInformation); | ||
@@ -194,6 +208,7 @@ /** @internal */ | ||
getPuppetMxidInfo(puppetId: number): Promise<IMxidInfo | null>; | ||
trackConnectionStatus(puppetId: number, isConnected: boolean): void; | ||
/** | ||
* Send a status message either to the status message room or to a specified room | ||
*/ | ||
sendStatusMessage(puppetId: number | IRemoteRoom, msg: string): Promise<void>; | ||
sendStatusMessage(puppetId: number | IRemoteRoom, msg: string, isConnected?: boolean | null): Promise<void>; | ||
/** | ||
@@ -200,0 +215,0 @@ * Registers a custom command with the bot provisioner |
@@ -28,2 +28,4 @@ "use strict"; | ||
const yaml = require("js-yaml"); | ||
const prometheus = require("prom-client"); | ||
const express = require("express"); | ||
const events_1 = require("events"); | ||
@@ -56,2 +58,5 @@ const emotesyncroniser_1 = require("./emotesyncroniser"); | ||
const AVATAR_SIZE = 800; | ||
class BridgeMetrics { | ||
} | ||
exports.BridgeMetrics = BridgeMetrics; | ||
class PuppetBridge extends events_1.EventEmitter { | ||
@@ -80,4 +85,26 @@ constructor(registrationPath, configPath, prot) { | ||
this.hooks = {}; | ||
this.connectionMetricStatus = {}; | ||
this.delayedFunction = new delayedfunction_1.DelayedFunction(); | ||
this.mxcLookupLock = new lock_1.Lock(MXC_LOOKUP_LOCK_TIMEOUT); | ||
this.metrics = new BridgeMetrics(); | ||
this.metrics.room = new prometheus.Gauge({ | ||
name: "bridge_rooms_total", | ||
help: "Total rooms bridged to the remote network, by type and protocol", | ||
labelNames: ["type", "protocol"], | ||
}); | ||
this.metrics.puppet = new prometheus.Gauge({ | ||
name: "bridge_puppets_total", | ||
help: "Puppets linked to remote network, puppeted by matrix users", | ||
labelNames: ["protocol"], | ||
}); | ||
this.metrics.connected = new prometheus.Gauge({ | ||
name: "bridge_connected", | ||
help: "Users connected to the remote network", | ||
labelNames: ["protocol"], | ||
}); | ||
this.metrics.message = new prometheus.Counter({ | ||
name: "bridge_messages_total", | ||
help: "Total messages bridged into matrix, by type and protocol", | ||
labelNames: ["type", "protocol"], | ||
}); | ||
} | ||
@@ -147,2 +174,12 @@ /** @internal */ | ||
this.provisioningAPI = new provisioningapi_1.ProvisioningAPI(this); | ||
if (this.config.metrics.enabled) { | ||
prometheus.collectDefaultMetrics(); | ||
const metricsServer = express(); | ||
metricsServer.get(this.config.metrics.path, (req, res) => __awaiter(this, void 0, void 0, function* () { | ||
res.set("Content-Type", prometheus.register.contentType); | ||
const metrics = yield prometheus.register.metrics(); | ||
res.send(metrics); | ||
})); | ||
metricsServer.listen(this.config.metrics.port); | ||
} | ||
// pipe matrix-bot-sdk logging int ours | ||
@@ -310,2 +347,3 @@ const logMap = new Map(); | ||
const puppets = yield this.provisioner.getAll(); | ||
this.metrics.puppet.set({ protocol: this.protocol.id }, puppets.length); | ||
for (const p of puppets) { | ||
@@ -422,2 +460,3 @@ this.emit("puppetNew", p.puppetId, p.data); | ||
bridgeRoom(roomData) { | ||
var _a; | ||
return __awaiter(this, void 0, void 0, function* () { | ||
@@ -429,5 +468,9 @@ if (!this.hooks.createRoom) { | ||
const room = yield this.namespaceHandler.createRoom(roomData); | ||
if (!room || room.isDirect) { | ||
if (!room) { | ||
return; | ||
} | ||
this.metrics.room.inc({ type: room.isDirect ? "dm" : "group", protocol: (_a = this.protocol) === null || _a === void 0 ? void 0 : _a.id }); | ||
if (room.isDirect) { | ||
return; | ||
} | ||
log.info(`Got request to bridge room puppetId=${room.puppetId} roomId=${room.roomId}`); | ||
@@ -450,2 +493,3 @@ yield this.roomSync.getMxid(room); | ||
unbridgeRoom(room) { | ||
var _a; | ||
return __awaiter(this, void 0, void 0, function* () { | ||
@@ -457,2 +501,3 @@ if (!room) { | ||
yield this.roomSync.delete(room, true); | ||
this.metrics.room.dec({ type: room.isDirect ? "dm" : "group", protocol: (_a = this.protocol) === null || _a === void 0 ? void 0 : _a.id }); | ||
}); | ||
@@ -602,7 +647,22 @@ } | ||
} | ||
trackConnectionStatus(puppetId, isConnected) { | ||
if (Boolean(this.connectionMetricStatus[puppetId]) === isConnected) { | ||
return; | ||
} | ||
this.connectionMetricStatus[puppetId] = isConnected; | ||
if (isConnected) { | ||
this.metrics.connected.inc({ protocol: this.protocol.id }); | ||
} | ||
else { | ||
this.metrics.connected.dec({ protocol: this.protocol.id }); | ||
} | ||
} | ||
/** | ||
* Send a status message either to the status message room or to a specified room | ||
*/ | ||
sendStatusMessage(puppetId, msg) { | ||
sendStatusMessage(puppetId, msg, isConnected = null) { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
if (isConnected !== null) { | ||
this.trackConnectionStatus(typeof puppetId === "number" ? puppetId : puppetId.puppetId, isConnected); | ||
} | ||
yield this.botProvisioner.sendStatusMessage(puppetId, msg); | ||
@@ -631,2 +691,3 @@ }); | ||
yield this.remoteEventHandler.sendFileByType("m.file", params, thing, name); | ||
this.metrics.message.inc({ type: "file", protocol: this.protocol.id }); | ||
}); | ||
@@ -640,2 +701,3 @@ } | ||
yield this.remoteEventHandler.sendFileByType("m.video", params, thing, name); | ||
this.metrics.message.inc({ type: "video", protocol: this.protocol.id }); | ||
}); | ||
@@ -649,2 +711,3 @@ } | ||
yield this.remoteEventHandler.sendFileByType("m.audio", params, thing, name); | ||
this.metrics.message.inc({ type: "audio", protocol: this.protocol.id }); | ||
}); | ||
@@ -658,2 +721,3 @@ } | ||
yield this.remoteEventHandler.sendFileByType("m.image", params, thing, name); | ||
this.metrics.message.inc({ type: "image", protocol: this.protocol.id }); | ||
}); | ||
@@ -667,2 +731,3 @@ } | ||
yield this.remoteEventHandler.sendMessage(params, opts); | ||
this.metrics.message.inc({ type: "text", protocol: this.protocol.id }); | ||
}); | ||
@@ -676,2 +741,3 @@ } | ||
yield this.remoteEventHandler.sendEdit(params, eventId, opts, ix); | ||
this.metrics.message.inc({ type: "edit", protocol: this.protocol.id }); | ||
}); | ||
@@ -685,2 +751,3 @@ } | ||
yield this.remoteEventHandler.sendRedact(params, eventId); | ||
this.metrics.message.inc({ type: "redact", protocol: this.protocol.id }); | ||
}); | ||
@@ -702,2 +769,3 @@ } | ||
yield this.remoteEventHandler.sendReaction(params, eventId, reaction); | ||
this.metrics.message.inc({ type: "reaction", protocol: this.protocol.id }); | ||
}); | ||
@@ -704,0 +772,0 @@ } |
@@ -24,3 +24,4 @@ /// <reference types="node" /> | ||
private prepareSend; | ||
private preprocessBody; | ||
private preprocessMessageEvent; | ||
} |
@@ -29,2 +29,3 @@ "use strict"; | ||
const unescapeHtml = require("unescape"); | ||
const prometheus = require("prom-client"); | ||
const blurhash_1 = require("blurhash"); | ||
@@ -40,2 +41,9 @@ const Canvas = require("canvas"); | ||
this.ghostInviteCache = new timedcache_1.TimedCache(PUPPET_INVITE_CACHE_LIFETIME); | ||
this.bridge.metrics.remoteUpdateBucket = new prometheus.Histogram({ | ||
name: "bridge_remote_update_seconds", | ||
help: "Time spent processing updates from the remote network, by protocol and type", | ||
labelNames: ["protocol", "type"], | ||
// tslint:disable-next-line no-magic-numbers | ||
buckets: [0.002, 0.005, 0.01, 0.25, 0.5, 0.75, 1, 1.5, 2, 3, 5, 7, 10], | ||
}); | ||
} | ||
@@ -146,2 +154,5 @@ setUserPresence(user, presence) { | ||
} | ||
const stopTimer = this.bridge.metrics.remoteUpdateBucket.startTimer({ | ||
protocol: this.bridge.protocol.id, | ||
}); | ||
log.info(`Received message from ${params.user.userId} to send to ${params.room.roomId}`); | ||
@@ -175,2 +186,3 @@ this.preprocessMessageEvent(opts); | ||
yield this.bridge.typingHandler.set(yield client.getUserId(), mxid, false); | ||
stopTimer({ type: msgtype }); | ||
}); | ||
@@ -183,2 +195,5 @@ } | ||
} | ||
const stopTimer = this.bridge.metrics.remoteUpdateBucket.startTimer({ | ||
protocol: this.bridge.protocol.id, | ||
}); | ||
log.info(`Received edit from ${params.user.userId} to send to ${params.room.roomId}`); | ||
@@ -239,2 +254,3 @@ this.preprocessMessageEvent(opts); | ||
yield this.bridge.typingHandler.set(yield client.getUserId(), mxid, false); | ||
stopTimer({ type: msgtype }); | ||
}); | ||
@@ -261,2 +277,5 @@ } | ||
} | ||
const stopTimer = this.bridge.metrics.remoteUpdateBucket.startTimer({ | ||
protocol: this.bridge.protocol.id, | ||
}); | ||
log.info(`Received reply from ${params.user.userId} to send to ${params.room.roomId}`); | ||
@@ -299,4 +318,4 @@ this.preprocessMessageEvent(opts); | ||
} | ||
const bodyParts = info.message.body.split("\n"); | ||
bodyParts[0] = `${info.message.emote ? "* " : ""}<${info.user.mxid}> ${bodyParts[0]}`; | ||
const bodyParts = this.preprocessBody(info.message.body).split("\n"); | ||
bodyParts[0] = `${info.message.emote ? "* " : ""}<${this.preprocessBody(info.user.mxid)}> ${bodyParts[0]}`; | ||
send.body = `${bodyParts.map((l) => `> ${l}`).join("\n")}\n\n${send.body}`; | ||
@@ -322,3 +341,3 @@ const matrixReplyRegex = /^<mx-reply>.*<\/mx-reply>/gs; | ||
} | ||
const plainHeader = `> <${info.user.mxid}> sent ${msg}.\n\n`; | ||
const plainHeader = `> <${this.preprocessBody(info.user.mxid)}> sent ${msg}.\n\n`; | ||
send.body = plainHeader + send.body; | ||
@@ -350,2 +369,3 @@ const richHeader = `<mx-reply><blockquote> | ||
yield this.bridge.typingHandler.set(yield client.getUserId(), mxid, false); | ||
stopTimer({ type: msgtype }); | ||
}); | ||
@@ -391,2 +411,5 @@ } | ||
} | ||
const stopTimer = this.bridge.metrics.remoteUpdateBucket.startTimer({ | ||
protocol: this.bridge.protocol.id, | ||
}); | ||
log.info(`Received file to send from ${params.user.userId} in ${params.room.roomId}.`); | ||
@@ -543,2 +566,3 @@ log.verbose(`thing=${typeof thing === "string" ? thing : "<Buffer>"} name=${name}`); | ||
yield this.bridge.typingHandler.set(yield client.getUserId(), mxid, false); | ||
stopTimer({ type: msgtype }); | ||
}); | ||
@@ -635,7 +659,11 @@ } | ||
} | ||
preprocessMessageEvent(opts) { | ||
preprocessBody(body) { | ||
for (const homeserver of this.bridge.config.bridge.stripHomeservers) { | ||
const urlRegex = homeserver.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); | ||
opts.body = opts.body.replace(new RegExp(`@([\x21-\x39\x3b-\x7e]+):${urlRegex}`, "g"), "@$1"); | ||
body = body.replace(new RegExp(`@([\x21-\x39\x3b-\x7e]+):${urlRegex}`, "g"), "@$1"); | ||
} | ||
return body; | ||
} | ||
preprocessMessageEvent(opts) { | ||
opts.body = this.preprocessBody(opts.body); | ||
if (!opts.formattedBody) { | ||
@@ -642,0 +670,0 @@ return; |
@@ -36,2 +36,18 @@ "use strict"; | ||
this.mxidLock = new lock_1.Lock(MXID_LOOKUP_LOCK_TIMEOUT); | ||
if (this.bridge.config.metrics.enabled) { | ||
const roomMetricsInit = (rooms) => { | ||
this.bridge.metrics.room.set({ | ||
type: "dm", | ||
protocol: this.bridge.protocol.id, | ||
}, rooms.filter((room) => room.isDirect).length); | ||
this.bridge.metrics.room.set({ | ||
type: "group", | ||
protocol: this.bridge.protocol.id, | ||
}, rooms.filter((room) => !room.isDirect).length); | ||
}; | ||
this.roomStore.getAll() | ||
.catch((err) => log.error("could not get room store")) | ||
.then(roomMetricsInit) | ||
.catch((err) => log.warn("could not init room metrics")); | ||
} | ||
} | ||
@@ -38,0 +54,0 @@ getRoomOp(room) { |
@@ -188,2 +188,3 @@ "use strict"; | ||
openDatabase() { | ||
var _a, _b, _c, _d, _e, _f, _g; | ||
return __awaiter(this, void 0, void 0, function* () { | ||
@@ -200,9 +201,9 @@ if (this.config.connString) { | ||
this.db.Open(); | ||
this.pRoomStore = new roomstore_1.DbRoomStore(this.db); | ||
this.pUserStore = new userstore_1.DbUserStore(this.db); | ||
this.pGroupStore = new groupstore_1.DbGroupStore(this.db); | ||
this.pPuppetStore = new puppetstore_1.DbPuppetStore(this.db); | ||
this.pEventStore = new eventstore_1.DbEventStore(this.db); | ||
this.pReactionStore = new reactionstore_1.DbReactionStore(this.db); | ||
this.pEmoteStore = new emotestore_1.DbEmoteStore(this.db); | ||
this.pRoomStore = new roomstore_1.DbRoomStore(this.db, undefined, (_a = this.bridge.protocol) === null || _a === void 0 ? void 0 : _a.id); | ||
this.pUserStore = new userstore_1.DbUserStore(this.db, undefined, (_b = this.bridge.protocol) === null || _b === void 0 ? void 0 : _b.id); | ||
this.pGroupStore = new groupstore_1.DbGroupStore(this.db, undefined, (_c = this.bridge.protocol) === null || _c === void 0 ? void 0 : _c.id); | ||
this.pPuppetStore = new puppetstore_1.DbPuppetStore(this.db, undefined, (_d = this.bridge.protocol) === null || _d === void 0 ? void 0 : _d.id); | ||
this.pEventStore = new eventstore_1.DbEventStore(this.db, (_e = this.bridge.protocol) === null || _e === void 0 ? void 0 : _e.id); | ||
this.pReactionStore = new reactionstore_1.DbReactionStore(this.db, (_f = this.bridge.protocol) === null || _f === void 0 ? void 0 : _f.id); | ||
this.pEmoteStore = new emotestore_1.DbEmoteStore(this.db, (_g = this.bridge.protocol) === null || _g === void 0 ? void 0 : _g.id); | ||
} | ||
@@ -209,0 +210,0 @@ catch (ex) { |
@@ -28,2 +28,3 @@ "use strict"; | ||
const lock_1 = require("./structures/lock"); | ||
const prometheus = require("prom-client"); | ||
const log = new log_1.Log("UserSync"); | ||
@@ -41,2 +42,14 @@ // tslint:disable no-magic-numbers | ||
this.roomOverrideLock = new lock_1.Lock(ROOM_OVERRIDE_LOCK_TIMEOUT); | ||
const that = this; | ||
this.bridge.metrics.remoteUser = new prometheus.Gauge({ | ||
name: "bridge_remote_users_total", | ||
help: "Total number of users on the remote network", | ||
labelNames: ["protocol"], | ||
collect() { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
const remoteUsers = yield that.userStore.getAll(); | ||
this.set({ protocol: that.bridge.protocol.id }, remoteUsers.length); | ||
}); | ||
}, | ||
}); | ||
} | ||
@@ -43,0 +56,0 @@ getClientFromTokenCallback(token) { |
{ | ||
"name": "mx-puppet-bridge", | ||
"version": "0.1.3", | ||
"version": "0.1.4", | ||
"description": "Matrix Puppeting Bridge library", | ||
@@ -36,2 +36,3 @@ "repository": { | ||
"pg-promise": "^10.5.0", | ||
"prom-client": "^13.0.0", | ||
"unescape": "^1.0.1", | ||
@@ -38,0 +39,0 @@ "uuid": "^3.4.0", |
@@ -21,2 +21,3 @@ /* | ||
public database: DatabaseConfig = new DatabaseConfig(); | ||
public metrics: MetricsConfig = new MetricsConfig(); | ||
public provisioning: ProvisioningConfig = new ProvisioningConfig(); | ||
@@ -82,2 +83,8 @@ public presence: PresenceConfig = new PresenceConfig(); | ||
export class MetricsConfig { | ||
public enabled: boolean = false; | ||
public port: number = 8000; | ||
public path: string = "/metrics"; | ||
} | ||
export class DatabaseConfig { | ||
@@ -84,0 +91,0 @@ public connString: string; |
@@ -17,2 +17,4 @@ /* | ||
import * as prometheus from "prom-client"; | ||
type SQLTYPES = number | boolean | string | null; | ||
@@ -30,2 +32,3 @@ | ||
type: string; | ||
latency: prometheus.Histogram<string>; | ||
Open(): void; | ||
@@ -32,0 +35,0 @@ Get(sql: string, parameters?: ISqlCommandParameters): Promise<ISqlRow|null>; |
@@ -23,3 +23,4 @@ /* | ||
private db: IDatabaseConnector, | ||
) { } | ||
private protocol: string = "unknown", | ||
) {} | ||
@@ -40,2 +41,3 @@ public newData(puppetId: number, roomId: string | null, emoteId: string): IEmoteStoreEntry { | ||
public async get(puppetId: number, roomId: string | null, emoteId: string): Promise<IEmoteStoreEntry | null> { | ||
const stopTimer = this.db.latency.startTimer(this.labels("select")); | ||
if (roomId) { | ||
@@ -48,2 +50,3 @@ const row = await this.db.Get( | ||
}); | ||
stopTimer(); | ||
return this.getFromRow(row); | ||
@@ -56,2 +59,3 @@ } else { | ||
}); | ||
stopTimer(); | ||
return this.getFromRow(row); | ||
@@ -62,2 +66,3 @@ } | ||
public async getByMxc(puppetId: number, roomId: string | null, mxid: string): Promise<IEmoteStoreEntry | null> { | ||
const stopTimer = this.db.latency.startTimer(this.labels("select_by_mxc")); | ||
if (roomId) { | ||
@@ -70,2 +75,3 @@ const row = await this.db.Get( | ||
}); | ||
stopTimer(); | ||
return this.getFromRow(row); | ||
@@ -78,2 +84,3 @@ } else { | ||
}); | ||
stopTimer(); | ||
return this.getFromRow(row); | ||
@@ -84,2 +91,3 @@ } | ||
public async getForRoom(puppetId: number, roomId: string): Promise<IEmoteStoreEntry[]> { | ||
const stopTimer = this.db.latency.startTimer(this.labels("select_by_room")); | ||
const rows = await this.db.All("SELECT * FROM emote_store WHERE puppet_id = $pid AND room_id = $rid", { | ||
@@ -96,2 +104,3 @@ pid: puppetId, | ||
} | ||
stopTimer(); | ||
return result; | ||
@@ -101,2 +110,3 @@ } | ||
public async set(data: IEmoteStoreEntry) { | ||
const stopTimer = this.db.latency.startTimer(this.labels("insert_update")); | ||
let exists: ISqlRow | null = null; | ||
@@ -159,2 +169,3 @@ if (data.roomId) { | ||
}); | ||
stopTimer(); | ||
} | ||
@@ -177,2 +188,11 @@ | ||
} | ||
private labels(queryName: string): object { | ||
return { | ||
protocol: this.protocol, | ||
engine: this.db.type, | ||
table: "emote_store", | ||
type: queryName, | ||
}; | ||
} | ||
} |
@@ -22,5 +22,7 @@ /* | ||
private db: IDatabaseConnector, | ||
private protocol: string = "unknown", | ||
) { } | ||
public async insert(puppetId: number, roomId: string, matrixId: string, remoteId: string) { | ||
const stopTimer = this.db.latency.startTimer(this.labels("insert")); | ||
await this.db.Run("INSERT INTO event_store (puppet_id, room_id, matrix_id, remote_id) VALUES ($p, $room, $m, $r)", { | ||
@@ -32,5 +34,7 @@ p: puppetId, | ||
}); | ||
stopTimer(); | ||
} | ||
public async remove(puppetId: number, roomId: string, remoteId: string) { | ||
const stopTimer = this.db.latency.startTimer(this.labels("remove")); | ||
await this.db.Run("DELETE FROM event_store WHERE puppet_id = $p AND room_id = $room AND remote_id = $r", { | ||
@@ -41,5 +45,7 @@ p: puppetId, | ||
}); | ||
stopTimer(); | ||
} | ||
public async getMatrix(puppetId: number, roomId: string, remoteId: string): Promise<string[]> { | ||
const stopTimer = this.db.latency.startTimer(this.labels("select_matrix")); | ||
const result: string[] = []; | ||
@@ -54,2 +60,3 @@ const rows = await this.db.All("SELECT * FROM event_store WHERE puppet_id=$p AND room_id = $room AND remote_id=$r", { | ||
} | ||
stopTimer(); | ||
return result; | ||
@@ -59,2 +66,3 @@ } | ||
public async getRemote(puppetId: number, roomId: string, matrixId: string): Promise<string[]> { | ||
const stopTimer = this.db.latency.startTimer(this.labels("select_remote")); | ||
const result: string[] = []; | ||
@@ -70,4 +78,14 @@ const rows = await this.db.All( | ||
} | ||
stopTimer(); | ||
return result; | ||
} | ||
private labels(queryName: string): object { | ||
return { | ||
protocol: this.protocol, | ||
engine: this.db.type, | ||
table: "event_store", | ||
type: queryName, | ||
}; | ||
} | ||
} |
@@ -26,7 +26,10 @@ /* | ||
private groupsCache: TimedCache<string, IGroupStoreEntry>; | ||
private protocol: string; | ||
constructor( | ||
private db: IDatabaseConnector, | ||
cache: boolean = true, | ||
protocolId: string = "unknown", | ||
) { | ||
this.groupsCache = new TimedCache(cache ? GROUP_CACHE_LIFETIME : 0); | ||
this.protocol = protocolId; | ||
} | ||
@@ -48,2 +51,3 @@ | ||
): Promise<IGroupStoreEntry | null> { | ||
const stopTimer = this.db.latency.startTimer(this.labels("select_by_remote")); | ||
if (!ignoreCache) { | ||
@@ -60,6 +64,9 @@ const cached = this.groupsCache.get(`${puppetId};${groupId}`); | ||
}); | ||
return await this.getFromRow(row); | ||
const result = await this.getFromRow(row); | ||
stopTimer(); | ||
return result; | ||
} | ||
public async getByPuppetId(puppetId: number): Promise<IGroupStoreEntry[]> { | ||
const stopTimer = this.db.latency.startTimer(this.labels("select_by_puppet")); | ||
const rows = await this.db.All( | ||
@@ -76,2 +83,3 @@ "SELECT * FROM group_store WHERE puppet_id = $puppetId", { | ||
} | ||
stopTimer(); | ||
return results; | ||
@@ -81,9 +89,13 @@ } | ||
public async getByMxid(mxid: string): Promise<IGroupStoreEntry | null> { | ||
const stopTimer = this.db.latency.startTimer(this.labels("select_by_mxid")); | ||
const row = await this.db.Get( | ||
"SELECT * FROM group_store WHERE mxid = $mxid", { mxid }, | ||
); | ||
return await this.getFromRow(row); | ||
const result = await this.getFromRow(row); | ||
stopTimer(); | ||
return result; | ||
} | ||
public async set(data: IGroupStoreEntry) { | ||
const stopTimer = this.db.latency.startTimer(this.labels("insert_update")); | ||
// first de-dupe the room IDs | ||
@@ -202,5 +214,7 @@ const uniqueRoomIds: string[] = []; | ||
this.groupsCache.set(`${data.puppetId};${data.groupId}`, data); | ||
stopTimer(); | ||
} | ||
public async delete(data: IGroupStoreEntry) { | ||
const stopTimer = this.db.latency.startTimer(this.labels("delete")); | ||
await this.db.Run( | ||
@@ -215,5 +229,7 @@ "DELETE FROM group_store WHERE mxid = $mxid", { mxid: data.mxid }, | ||
this.groupsCache.delete(`${data.puppetId};${data.groupId}`); | ||
stopTimer(); | ||
} | ||
private async getFromRow(row: ISqlRow | null): Promise<IGroupStoreEntry | null> { | ||
const stopTimer = this.db.latency.startTimer(this.labels("select_from_row")); | ||
if (!row) { | ||
@@ -246,4 +262,14 @@ return null; | ||
this.groupsCache.set(`${data.puppetId};${data.groupId}`, data); | ||
stopTimer(); | ||
return data; | ||
} | ||
private labels(queryName: string): object { | ||
return { | ||
protocol: this.protocol, | ||
engine: this.db.type, | ||
table: "group_store", | ||
type: queryName, | ||
}; | ||
} | ||
} |
@@ -20,2 +20,3 @@ /* | ||
import { IDatabaseConnector, ISqlCommandParameters, ISqlRow } from "./connector"; | ||
import * as prometheus from "prom-client"; | ||
const log = new Log("Postgres"); | ||
@@ -35,2 +36,3 @@ | ||
public type = "postgres"; | ||
public latency: prometheus.Histogram<string>; | ||
@@ -40,3 +42,9 @@ // tslint:disable-next-line no-any | ||
constructor(private connectionString: string) { | ||
this.latency = new prometheus.Histogram({ | ||
name: "bridge_database_query_seconds", | ||
help: "Time spent querying the database engine", | ||
labelNames: ["protocol", "engine", "type", "table"], | ||
// tslint:disable-next-line no-magic-numbers | ||
buckets: [0.002, 0.005, 0.0075, 0.01, 0.02, 0.05, 0.1, 0.2, 0.5, 1, 2, 5], | ||
}); | ||
} | ||
@@ -43,0 +51,0 @@ public Open() { |
@@ -55,5 +55,7 @@ /* | ||
private allPuppetIds: Set<number> | null; | ||
private protocol: string; | ||
constructor( | ||
private db: IDatabaseConnector, | ||
cache: boolean = true, | ||
protocol: string = "unknown", | ||
) { | ||
@@ -64,9 +66,13 @@ this.mxidCache = new TimedCache(cache ? PUPPET_CACHE_LIFETIME : 0); | ||
this.allPuppetIds = null; | ||
this.protocol = protocol; | ||
} | ||
public async deleteStatusRoom(mxid: string) { | ||
const stopTimer = this.db.latency.startTimer(this.labels("update_status")); | ||
await this.db.Run("UPDATE puppet_mxid_store SET status_room = '' WHERE status_room = $mxid", { mxid }); | ||
stopTimer(); | ||
} | ||
public async getMxidInfo(puppetMxid: string): Promise<IMxidInfo | null> { | ||
const stopTimer = this.db.latency.startTimer(this.labels("get_mx_info")); | ||
const row = await this.db.Get("SELECT * FROM puppet_mxid_store WHERE puppet_mxid=$id", { id: puppetMxid }); | ||
@@ -76,2 +82,3 @@ if (!row) { | ||
} | ||
stopTimer(); | ||
return { | ||
@@ -109,2 +116,3 @@ puppetMxid, | ||
public async setMxidInfo(puppet: IMxidInfo) { | ||
const stopTimer = this.db.latency.startTimer(this.labels("set_mxid_info")); | ||
const exists = await this.db.Get("SELECT * FROM puppet_mxid_store WHERE puppet_mxid=$id", { id: puppet.puppetMxid }); | ||
@@ -141,5 +149,7 @@ let query = ""; | ||
}); | ||
stopTimer(); | ||
} | ||
public async getAll(): Promise<IPuppet[]> { | ||
const stopTimer = this.db.latency.startTimer(this.labels("select_all")); | ||
let result: IPuppet[] = []; | ||
@@ -170,2 +180,3 @@ if (this.allPuppetIds) { | ||
} | ||
stopTimer(); | ||
return result; | ||
@@ -175,2 +186,3 @@ } | ||
public async getForMxid(puppetMxid: string): Promise<IPuppet[]> { | ||
const stopTimer = this.db.latency.startTimer(this.labels("select_for_mx")); | ||
const result: IPuppet[] = []; | ||
@@ -184,2 +196,3 @@ const rows = await this.db.All("SELECT * FROM puppet_store WHERE puppet_mxid=$mxid", { mxid: puppetMxid }); | ||
} | ||
stopTimer(); | ||
return result; | ||
@@ -189,2 +202,3 @@ } | ||
public async get(puppetId: number): Promise<IPuppet | null> { | ||
const stopTimer = this.db.latency.startTimer(this.labels("select")); | ||
const cached = this.puppetCache.get(puppetId); | ||
@@ -198,2 +212,3 @@ if (cached) { | ||
} | ||
stopTimer(); | ||
return this.getRow(row); | ||
@@ -203,2 +218,3 @@ } | ||
public async getMxid(puppetId: number): Promise<string> { | ||
const stopTimer = this.db.latency.startTimer(this.labels("select_mxid")); | ||
const cached = this.mxidCache.get(puppetId); | ||
@@ -214,2 +230,3 @@ if (cached) { | ||
this.mxidCache.set(puppetId, mxid); | ||
stopTimer(); | ||
return mxid; | ||
@@ -219,2 +236,3 @@ } | ||
public async setUserId(puppetId: number, userId: string) { | ||
const stopTimer = this.db.latency.startTimer(this.labels("update_uid")); | ||
await this.db.Run("UPDATE puppet_store SET user_id=$uid WHERE puppet_id=$pid", { | ||
@@ -225,5 +243,7 @@ uid: userId, | ||
this.puppetCache.delete(puppetId); | ||
stopTimer(); | ||
} | ||
public async setData(puppetId: number, data: IPuppetData) { | ||
const stopTimer = this.db.latency.startTimer(this.labels("update_data")); | ||
let dataStr = ""; | ||
@@ -241,5 +261,7 @@ try { | ||
this.puppetCache.delete(puppetId); | ||
stopTimer(); | ||
} | ||
public async setType(puppetId: number, type: PuppetType) { | ||
const stopTimer = this.db.latency.startTimer(this.labels("update_type")); | ||
await this.db.Run("UPDATE puppet_store SET type=$t WHERE puppet_id=$id", { | ||
@@ -250,5 +272,7 @@ id: puppetId, | ||
this.puppetCache.delete(puppetId); | ||
stopTimer(); | ||
} | ||
public async setIsPublic(puppetId: number, isPublic: boolean) { | ||
const stopTimer = this.db.latency.startTimer(this.labels("update_visibility")); | ||
await this.db.Run("UPDATE puppet_store SET is_public=$p WHERE puppet_id=$id", { | ||
@@ -259,5 +283,7 @@ id: puppetId, | ||
this.puppetCache.delete(puppetId); | ||
stopTimer(); | ||
} | ||
public async setAutoinvite(puppetId: number, autoinvite: boolean) { | ||
const stopTimer = this.db.latency.startTimer(this.labels("update_autoinvite")); | ||
await this.db.Run("UPDATE puppet_store SET autoinvite=$a WHERE puppet_id=$id", { | ||
@@ -268,5 +294,7 @@ id: puppetId, | ||
this.puppetCache.delete(puppetId); | ||
stopTimer(); | ||
} | ||
public async setIsGlobalNamespace(puppetId: number, isGlobalNamespace: boolean) { | ||
const stopTimer = this.db.latency.startTimer(this.labels("update_namespace")); | ||
await this.db.Run("UPDATE puppet_store SET is_global_namespace=$is WHERE puppet_id=$id", { | ||
@@ -277,2 +305,3 @@ id: puppetId, | ||
this.puppetCache.delete(puppetId); | ||
stopTimer(); | ||
} | ||
@@ -286,2 +315,3 @@ | ||
): Promise<number> { | ||
const stopTimer = this.db.latency.startTimer(this.labels("insert")); | ||
let dataStr = ""; | ||
@@ -307,2 +337,3 @@ try { | ||
this.allPuppetIds = null; | ||
stopTimer(); | ||
return puppetId; | ||
@@ -312,2 +343,3 @@ } | ||
public async delete(puppetId: number) { | ||
const stopTimer = this.db.latency.startTimer(this.labels("delete")); | ||
await this.db.Run("DELETE FROM puppet_store WHERE puppet_id=$id", { id: puppetId }); | ||
@@ -317,5 +349,7 @@ this.mxidCache.delete(puppetId); | ||
this.allPuppetIds = null; | ||
stopTimer(); | ||
} | ||
public async isGhostInRoom(ghostMxid: string, roomMxid: string): Promise<boolean> { | ||
const stopTimer = this.db.latency.startTimer(this.labels("select_ghost_in_room")); | ||
const exists = await this.db.Get( | ||
@@ -327,2 +361,3 @@ "SELECT * FROM ghosts_joined_chans WHERE ghost_mxid = $ghostMxid AND chan_mxid = $roomMxid" | ||
}); | ||
stopTimer(); | ||
return exists ? true : false; | ||
@@ -332,2 +367,3 @@ } | ||
public async joinGhostToRoom(ghostMxid: string, roomMxid: string) { | ||
const stopTimer = this.db.latency.startTimer(this.labels("insert_ghost_in_room")); | ||
if (await this.isGhostInRoom(ghostMxid, roomMxid)) { | ||
@@ -340,5 +376,7 @@ return; | ||
}); | ||
stopTimer(); | ||
} | ||
public async getGhostsInRoom(room: string): Promise<string[]> { | ||
const stopTimer = this.db.latency.startTimer(this.labels("select_all_ghost_in_room")); | ||
const result: string[] = []; | ||
@@ -349,2 +387,3 @@ const rows = await this.db.All("SELECT * FROM ghosts_joined_chans WHERE chan_mxid = $room", { room }); | ||
} | ||
stopTimer(); | ||
return result; | ||
@@ -354,2 +393,3 @@ } | ||
public async getRoomsOfGhost(ghost: string): Promise<string[]> { | ||
const stopTimer = this.db.latency.startTimer(this.labels("select_all_rooms_of_ghost")); | ||
const result: string[] = []; | ||
@@ -360,2 +400,3 @@ const rows = await this.db.All("SELECT * FROM ghosts_joined_chans WHERE ghost_mxid = $ghost", { ghost }); | ||
} | ||
stopTimer(); | ||
return result; | ||
@@ -365,6 +406,9 @@ } | ||
public async emptyGhostsInRoom(room: string) { | ||
const stopTimer = this.db.latency.startTimer(this.labels("delete_ghosts_in_room")); | ||
await this.db.Run("DELETE FROM ghosts_joined_chans WHERE chan_mxid = $room", { room }); | ||
stopTimer(); | ||
} | ||
public async leaveGhostFromRoom(ghostMxid: string, roomMxid: string) { | ||
const stopTimer = this.db.latency.startTimer(this.labels("delete_ghost_in_room")); | ||
await this.db.Run("DELETE FROM ghosts_joined_chans " + | ||
@@ -375,2 +419,3 @@ "WHERE ghost_mxid = $g AND chan_mxid = $c", { | ||
}); | ||
stopTimer(); | ||
} | ||
@@ -397,2 +442,11 @@ | ||
} | ||
private labels(queryName: string): object { | ||
return { | ||
protocol: this.protocol, | ||
engine: this.db.type, | ||
table: "puppet_store", | ||
type: queryName, | ||
}; | ||
} | ||
} |
@@ -31,5 +31,7 @@ /* | ||
private db: IDatabaseConnector, | ||
private protocol: string = "unknown", | ||
) { } | ||
public async exists(data: IReactionStoreEntry): Promise<boolean> { | ||
const stopTimer = this.db.latency.startTimer(this.labels("select_exists")); | ||
const exists = await this.db.Get( | ||
@@ -44,2 +46,3 @@ `SELECT 1 FROM reaction_store WHERE puppet_id = $pid AND user_id = $uid | ||
}); | ||
stopTimer(); | ||
return exists ? true : false; | ||
@@ -49,2 +52,3 @@ } | ||
public async insert(data: IReactionStoreEntry): Promise<boolean> { | ||
const stopTimer = this.db.latency.startTimer(this.labels("insert")); | ||
if (await this.exists(data)) { | ||
@@ -63,2 +67,3 @@ return false; | ||
}); | ||
stopTimer(); | ||
return true; | ||
@@ -68,5 +73,7 @@ } | ||
public async getFromReactionMxid(reactionMxid: string): Promise<IReactionStoreEntry | null> { | ||
const stopTimer = this.db.latency.startTimer(this.labels("select_by_reaction_mxid")); | ||
const row = await this.db.Get( | ||
"SELECT * FROM reaction_store WHERE reaction_mxid = $reactionMxid", { reactionMxid }, | ||
); | ||
stopTimer(); | ||
return this.getFromRow(row); | ||
@@ -76,2 +83,3 @@ } | ||
public async getFromKey(data: IReactionStoreEntry): Promise<IReactionStoreEntry | null> { | ||
const stopTimer = this.db.latency.startTimer(this.labels("select_by_key")); | ||
const row = await this.db.Get( | ||
@@ -86,2 +94,3 @@ `SELECT * FROM reaction_store WHERE puppet_id = $pid AND user_id = $uid AND room_id = $rid | ||
}); | ||
stopTimer(); | ||
return this.getFromRow(row); | ||
@@ -91,2 +100,3 @@ } | ||
public async getForEvent(puppetId: number, eventId: string): Promise<IReactionStoreEntry[]> { | ||
const stopTimer = this.db.latency.startTimer(this.labels("select_for_event")); | ||
const rows = await this.db.All( | ||
@@ -103,2 +113,3 @@ "SELECT * FROM reaction_store WHERE puppet_id = $puppetId AND event_id = $eventId", | ||
} | ||
stopTimer(); | ||
return result; | ||
@@ -108,9 +119,13 @@ } | ||
public async delete(reactionMxid: string) { | ||
const stopTimer = this.db.latency.startTimer(this.labels("delete")); | ||
await this.db.Run("DELETE FROM reaction_store WHERE reaction_mxid = $reactionMxid", { reactionMxid }); | ||
stopTimer(); | ||
} | ||
public async deleteForEvent(puppetId: number, eventId: string) { | ||
const stopTimer = this.db.latency.startTimer(this.labels("delete_for_event")); | ||
await this.db.Run("DELETE FROM reaction_store WHERE puppet_id = $puppetId AND event_id = $eventId", | ||
{ puppetId, eventId }, | ||
); | ||
stopTimer(); | ||
} | ||
@@ -131,2 +146,11 @@ | ||
} | ||
private labels(queryName: string): object { | ||
return { | ||
protocol: this.protocol, | ||
engine: this.db.type, | ||
table: "reaction_store", | ||
type: queryName, | ||
}; | ||
} | ||
} |
@@ -28,5 +28,7 @@ /* | ||
private opCache: TimedCache<string, string>; | ||
private protocol: string; | ||
constructor( | ||
private db: IDatabaseConnector, | ||
cache: boolean = true, | ||
protocol: string = "unknown", | ||
) { | ||
@@ -36,2 +38,3 @@ this.remoteCache = new TimedCache(cache ? ROOM_CACHE_LIFETIME : 0); | ||
this.opCache = new TimedCache(cache ? ROOM_CACHE_LIFETIME : 0); | ||
this.protocol = protocol; | ||
} | ||
@@ -51,2 +54,3 @@ | ||
public async getAll(): Promise<IRoomStoreEntry[]> { | ||
const stopTimer = this.db.latency.startTimer(this.labels("select_all")); | ||
const rows = await this.db.All("SELECT * FROM room_store"); | ||
@@ -60,2 +64,3 @@ const results: IRoomStoreEntry[] = []; | ||
} | ||
stopTimer(); | ||
return results; | ||
@@ -65,2 +70,3 @@ } | ||
public async getByRemote(puppetId: number, roomId: string): Promise<IRoomStoreEntry | null> { | ||
const stopTimer = this.db.latency.startTimer(this.labels("select_by_remote")); | ||
const cached = this.remoteCache.get(`${puppetId};${roomId}`); | ||
@@ -75,2 +81,3 @@ if (cached) { | ||
}); | ||
stopTimer(); | ||
return this.getFromRow(row); | ||
@@ -80,2 +87,3 @@ } | ||
public async getByPuppetId(puppetId: number): Promise<IRoomStoreEntry[]> { | ||
const stopTimer = this.db.latency.startTimer(this.labels("select_by_puppet")); | ||
const rows = await this.db.All( | ||
@@ -92,2 +100,3 @@ "SELECT * FROM room_store WHERE puppet_id = $puppet_id", { | ||
} | ||
stopTimer(); | ||
return results; | ||
@@ -97,2 +106,3 @@ } | ||
public async getByMxid(mxid: string): Promise<IRoomStoreEntry | null> { | ||
const stopTimer = this.db.latency.startTimer(this.labels("select_by_mxid")); | ||
const cached = this.mxidCache.get(mxid); | ||
@@ -105,2 +115,3 @@ if (cached) { | ||
); | ||
stopTimer(); | ||
return this.getFromRow(row); | ||
@@ -110,2 +121,3 @@ } | ||
public async set(data: IRoomStoreEntry) { | ||
const stopTimer = this.db.latency.startTimer(this.labels("insert_update")); | ||
const exists = await this.db.Get( | ||
@@ -178,5 +190,7 @@ "SELECT * FROM room_store WHERE mxid = $mxid", {mxid: data.mxid}, | ||
this.mxidCache.set(data.mxid, data); | ||
stopTimer(); | ||
} | ||
public async delete(data: IRoomStoreEntry) { | ||
const stopTimer = this.db.latency.startTimer(this.labels("delete")); | ||
await this.db.Run( | ||
@@ -191,5 +205,7 @@ "DELETE FROM room_store WHERE mxid = $mxid", { mxid: data.mxid }, | ||
this.opCache.delete(data.mxid); | ||
stopTimer(); | ||
} | ||
public async toGlobalNamespace(puppetId: number, roomId: string) { | ||
const stopTimer = this.db.latency.startTimer(this.labels("update_namespace")); | ||
const exists = await this.getByRemote(-1, roomId); | ||
@@ -210,5 +226,7 @@ if (exists) { | ||
this.opCache.delete(room.mxid); | ||
stopTimer(); | ||
} | ||
public async setRoomOp(roomMxid: string, userMxid: string) { | ||
const stopTimer = this.db.latency.startTimer(this.labels("update_room_op")); | ||
const row = await this.db.Get("SELECT * FROM chan_op WHERE chan_mxid=$chan LIMIT 1", { | ||
@@ -220,2 +238,3 @@ chan: roomMxid, | ||
// nothing to do, we are already set | ||
stopTimer(); | ||
return; | ||
@@ -232,5 +251,7 @@ } | ||
this.opCache.set(roomMxid, userMxid); | ||
stopTimer(); | ||
} | ||
public async getRoomOp(roomMxid: string): Promise<string|null> { | ||
const stopTimer = this.db.latency.startTimer(this.labels("select_room_op")); | ||
const cached = this.opCache.get(roomMxid); | ||
@@ -248,2 +269,3 @@ if (cached) { | ||
this.opCache.set(roomMxid, userMxid); | ||
stopTimer(); | ||
return userMxid; | ||
@@ -276,2 +298,11 @@ } | ||
} | ||
private labels(queryName: string): object { | ||
return { | ||
protocol: this.protocol, | ||
engine: this.db.type, | ||
table: "room_store", | ||
type: queryName, | ||
}; | ||
} | ||
} |
@@ -20,2 +20,3 @@ /* | ||
import { IDatabaseConnector, ISqlCommandParameters, ISqlRow } from "./connector"; | ||
import * as prometheus from "prom-client"; | ||
const log = new Log("SQLite3"); | ||
@@ -25,2 +26,3 @@ | ||
public type = "sqlite"; | ||
public latency: prometheus.Histogram<string>; | ||
private db: Database; | ||
@@ -30,2 +32,9 @@ private insertId: number; | ||
this.insertId = -1; | ||
this.latency = new prometheus.Histogram({ | ||
name: "bridge_database_query_seconds", | ||
help: "Time spent querying the database engine", | ||
labelNames: ["protocol", "engine", "type", "table"], | ||
// tslint:disable-next-line no-magic-numbers | ||
buckets: [0.002, 0.005, 0.0075, 0.01, 0.02, 0.05, 0.1, 0.2, 0.5, 1, 2, 5], | ||
}); | ||
} | ||
@@ -32,0 +41,0 @@ |
@@ -26,7 +26,10 @@ /* | ||
private usersCache: TimedCache<string, IUserStoreEntry>; | ||
private protocol: string; | ||
constructor( | ||
private db: IDatabaseConnector, | ||
cache: boolean = true, | ||
protocol: string = "unknown", | ||
) { | ||
this.usersCache = new TimedCache(cache ? USERS_CACHE_LIFETIME : 0); | ||
this.protocol = protocol; | ||
} | ||
@@ -41,3 +44,28 @@ | ||
public async getAll(): Promise<IUserStoreEntry[]> { | ||
const stopTimer = this.db.latency.startTimer(this.labels("select_all")); | ||
const results: IUserStoreEntry[] = []; | ||
const rows = await this.db.All( | ||
"SELECT * FROM user_store;", | ||
); | ||
if (!rows) { | ||
return []; | ||
} | ||
for (const r of rows) { | ||
const data = { | ||
name: r.name as string | null, | ||
userId: r.user_id as string, | ||
puppetId: r.puppet_id as number, | ||
avatarUrl: r.avatar_url as string | null, | ||
avatarMxc: r.avatar_mxc as string | null, | ||
avatarHash: r.avatar_hash as string | null, | ||
}; | ||
results.push(data); | ||
} | ||
stopTimer(); | ||
return results; | ||
} | ||
public async get(puppetId: number, userId: string): Promise<IUserStoreEntry | null> { | ||
const stopTimer = this.db.latency.startTimer(this.labels("select")); | ||
const cacheKey = `${puppetId};${userId}`; | ||
@@ -60,2 +88,3 @@ const cached = this.usersCache.get(cacheKey); | ||
this.usersCache.set(cacheKey, data); | ||
stopTimer(); | ||
return data; | ||
@@ -65,2 +94,3 @@ } | ||
public async set(data: IUserStoreEntry) { | ||
const stopTimer = this.db.latency.startTimer(this.labels("insert_update")); | ||
const exists = await this.db.Get( | ||
@@ -104,5 +134,7 @@ "SELECT 1 FROM user_store WHERE user_id = $id AND puppet_id = $pid", {id: data.userId, pid: data.puppetId}, | ||
this.usersCache.set(cacheKey, data); | ||
stopTimer(); | ||
} | ||
public async delete(data: IUserStoreEntry) { | ||
const stopTimer = this.db.latency.startTimer(this.labels("delete")); | ||
await this.db.Run("DELETE FROM user_store WHERE user_id = $user_id AND puppet_id = $puppet_id", { | ||
@@ -119,2 +151,3 @@ user_id: data.userId, | ||
this.usersCache.delete(cacheKey); | ||
stopTimer(); | ||
} | ||
@@ -135,2 +168,3 @@ | ||
): Promise<IUserStoreRoomOverrideEntry | null> { | ||
const stopTimer = this.db.latency.startTimer(this.labels("get_room_override")); | ||
const row = await this.db.Get( | ||
@@ -145,2 +179,3 @@ "SELECT * FROM user_store_room_override WHERE user_id = $uid AND puppet_id = $pid AND room_id = $rid", { | ||
} | ||
stopTimer(); | ||
return this.getRoomOverrideFromRow(row); | ||
@@ -150,2 +185,3 @@ } | ||
public async setRoomOverride(data: IUserStoreRoomOverrideEntry) { | ||
const stopTimer = this.db.latency.startTimer(this.labels("insert_update_room_override")); | ||
const exists = await this.db.Get( | ||
@@ -193,5 +229,7 @@ "SELECT 1 FROM user_store_room_override WHERE user_id = $uid AND puppet_id = $pid AND room_id = $rid", { | ||
}); | ||
stopTimer(); | ||
} | ||
public async getAllRoomOverrides(puppetId: number, userId: string): Promise<IUserStoreRoomOverrideEntry[]> { | ||
const stopTimer = this.db.latency.startTimer(this.labels("select_all_room_override")); | ||
const result: IUserStoreRoomOverrideEntry[] = []; | ||
@@ -209,2 +247,3 @@ const rows = await this.db.All( | ||
} | ||
stopTimer(); | ||
return result; | ||
@@ -228,2 +267,11 @@ } | ||
} | ||
private labels(queryName: string): object { | ||
return { | ||
protocol: this.protocol, | ||
engine: this.db.type, | ||
table: "user_store", | ||
type: queryName, | ||
}; | ||
} | ||
} |
@@ -24,2 +24,3 @@ /* | ||
import * as escapeHtml from "escape-html"; | ||
import * as prometheus from "prom-client"; | ||
import { IPuppet } from "./db/puppetstore"; | ||
@@ -42,2 +43,19 @@ | ||
this.memberInfoCache = {}; | ||
this.bridge.metrics.matrixEvent = new prometheus.Counter({ | ||
name: "bridge_matrix_events_total", | ||
help: "Total matrix events bridged to the remote network, by protocol and type", | ||
labelNames: ["protocol", "type"], | ||
}); | ||
this.bridge.metrics.matrixEventBucket = new prometheus.Histogram({ | ||
name: "bridge_matrix_event_seconds", | ||
help: "Time spent processing matrix events in seconds, by protocol", | ||
labelNames: ["protocol", "type"], | ||
// tslint:disable-next-line no-magic-numbers | ||
buckets: [0.002, 0.005, 0.01, 0.25, 0.5, 0.75, 1, 1.5, 2, 3, 5, 7, 10], | ||
}); | ||
this.bridge.metrics.matrixEventError = new prometheus.Counter({ | ||
name: "bridge_matrix_event_errors_total", | ||
help: "Errors encountered during matrix event processing", | ||
labelNames: ["protocol"], | ||
}); | ||
} | ||
@@ -48,2 +66,6 @@ | ||
this.bridge.AS.on("room.event", async (roomId: string, rawEvent: any) => { | ||
const stopTimer = this.bridge.metrics.matrixEventBucket.startTimer({ | ||
protocol: this.bridge.protocol.id, | ||
type: "room.event", | ||
}); | ||
try { | ||
@@ -53,2 +75,5 @@ await this.handleRoomEvent(roomId, new RoomEvent<RoomEventContent>(rawEvent)); | ||
log.error("Error handling appservice room.event", err.error || err.body || err); | ||
this.bridge.metrics.matrixEventError.inc({protocol: this.bridge.protocol.id}); | ||
} finally { | ||
stopTimer(); | ||
} | ||
@@ -58,2 +83,6 @@ }); | ||
this.bridge.AS.on("room.invite", async (roomId: string, rawEvent: any) => { | ||
const stopTimer = this.bridge.metrics.matrixEventBucket.startTimer({ | ||
protocol: this.bridge.protocol.id, | ||
type: "room.invite", | ||
}); | ||
try { | ||
@@ -63,2 +92,5 @@ await this.handleInviteEvent(roomId, new MembershipEvent(rawEvent)); | ||
log.error("Error handling appservice room.invite", err.error || err.body || err); | ||
this.bridge.metrics.matrixEventError.inc({protocol: this.bridge.protocol.id}); | ||
} finally { | ||
stopTimer(); | ||
} | ||
@@ -68,6 +100,13 @@ }); | ||
this.bridge.AS.on("query.room", async (alias: string, createRoom: any) => { | ||
const stopTimer = this.bridge.metrics.matrixEventBucket.startTimer({ | ||
protocol: this.bridge.protocol.id, | ||
type: "query.room", | ||
}); | ||
try { | ||
await this.handleRoomQuery(alias, createRoom); | ||
} catch (err) { | ||
this.bridge.metrics.matrixEventError.inc({protocol: this.bridge.protocol.id}); | ||
log.error("Error handling appservice query.room", err.error || err.body || err); | ||
} finally { | ||
stopTimer(); | ||
} | ||
@@ -77,2 +116,6 @@ }); | ||
this.bridge.AS.on("ephemeral.event", async (rawEvent: any) => { | ||
const stopTimer = this.bridge.metrics.matrixEventBucket.startTimer({ | ||
protocol: this.bridge.protocol.id, | ||
type: "ephemeral.event", | ||
}); | ||
try { | ||
@@ -93,2 +136,3 @@ switch (rawEvent.type) { | ||
} | ||
stopTimer(); | ||
}); | ||
@@ -142,2 +186,6 @@ } | ||
const membershipEvent = new MembershipEvent(event.raw); | ||
this.bridge.metrics.matrixEvent.inc({ | ||
protocol: this.bridge.protocol.id, | ||
type: `${event.type}.${membershipEvent.membership}`, | ||
}); | ||
switch (membershipEvent.membership) { | ||
@@ -157,2 +205,6 @@ case "join": | ||
await this.handleRedactEvent(roomId, evt); | ||
this.bridge.metrics.matrixEvent.inc({ | ||
protocol: this.bridge.protocol.id, | ||
type: event.type, | ||
}); | ||
return; | ||
@@ -383,2 +435,6 @@ } | ||
} | ||
this.bridge.metrics.matrixEvent.inc({ | ||
protocol: this.bridge.protocol.id, | ||
type: msgtype, | ||
}); | ||
} | ||
@@ -606,2 +662,3 @@ | ||
await this.bridge.userSync.getClient(parts); // create user, if it doesn't exist | ||
this.bridge.metrics.room?.inc({ type: roomData.isDirect ? "dm" : "group", protocol: this.bridge.protocol.id }); | ||
} | ||
@@ -608,0 +665,0 @@ |
@@ -324,2 +324,3 @@ /* | ||
await this.bridge.roomSync.rebridge(mxid, newRoomParts); | ||
this.bridge.metrics.room.inc({ type: "group", protocol: this.bridge.protocol.id }); | ||
} | ||
@@ -341,2 +342,3 @@ | ||
await this.bridge.roomSync.delete(roomParts, true); | ||
this.bridge.metrics.room.dec({ type: "group", protocol: this.bridge.protocol.id }); | ||
return true; | ||
@@ -401,3 +403,6 @@ } | ||
log.verbose(`No muting in direct rooms`); | ||
} else if (!await this.bridge.namespaceHandler.canSeeRoom(roomParts, userId)) { | ||
} else if (!await this.bridge.namespaceHandler.canSeeRoom({ | ||
puppetId: await this.bridge.namespaceHandler.getDbPuppetId(roomParts.puppetId), | ||
roomId: roomParts.roomId, | ||
}, userId)) { | ||
targetLevel = MUTED_POWER_LEVEL; | ||
@@ -469,3 +474,6 @@ } | ||
} | ||
if (await this.bridge.namespaceHandler.canSeeRoom(room, userId)) { | ||
if (await this.bridge.namespaceHandler.canSeeRoom({ | ||
roomId: room.roomId, | ||
puppetId: await this.bridge.namespaceHandler.getDbPuppetId(room.puppetId), | ||
}, userId)) { | ||
const client = (await this.bridge.roomSync.getRoomOp(room.mxid)) || this.bridge.botIntent.underlyingClient; | ||
@@ -472,0 +480,0 @@ try { |
@@ -25,2 +25,4 @@ /* | ||
import * as yaml from "js-yaml"; | ||
import * as prometheus from "prom-client"; | ||
import * as express from "express"; | ||
import { EventEmitter } from "events"; | ||
@@ -100,2 +102,14 @@ import { EmoteSyncroniser } from "./emotesyncroniser"; | ||
export class BridgeMetrics { | ||
public room: prometheus.Gauge<string>; | ||
public puppet: prometheus.Gauge<string>; | ||
public message: prometheus.Counter<string>; | ||
public remoteUser: prometheus.Gauge<string>; | ||
public matrixEvent: prometheus.Counter<string>; | ||
public matrixEventBucket: prometheus.Histogram<string>; | ||
public matrixEventError: prometheus.Counter<string>; | ||
public remoteUpdateBucket: prometheus.Histogram<string>; | ||
public connected: prometheus.Gauge<string>; | ||
} | ||
export class PuppetBridge extends EventEmitter { | ||
@@ -119,2 +133,3 @@ public emoteSync: EmoteSyncroniser; | ||
public namespaceHandler: NamespaceHandler; | ||
public metrics: BridgeMetrics; | ||
private appservice: Appservice; | ||
@@ -124,2 +139,3 @@ private mxcLookupLock: Lock<string>; | ||
private remoteEventHandler: RemoteEventHandler; | ||
private connectionMetricStatus: { [puppetId: number]: boolean }; | ||
@@ -149,4 +165,26 @@ constructor( | ||
this.hooks = {}; | ||
this.connectionMetricStatus = {}; | ||
this.delayedFunction = new DelayedFunction(); | ||
this.mxcLookupLock = new Lock(MXC_LOOKUP_LOCK_TIMEOUT); | ||
this.metrics = new BridgeMetrics(); | ||
this.metrics.room = new prometheus.Gauge({ | ||
name: "bridge_rooms_total", | ||
help: "Total rooms bridged to the remote network, by type and protocol", | ||
labelNames: ["type", "protocol"], | ||
}); | ||
this.metrics.puppet = new prometheus.Gauge({ | ||
name: "bridge_puppets_total", | ||
help: "Puppets linked to remote network, puppeted by matrix users", | ||
labelNames: ["protocol"], | ||
}); | ||
this.metrics.connected = new prometheus.Gauge({ | ||
name: "bridge_connected", | ||
help: "Users connected to the remote network", | ||
labelNames: ["protocol"], | ||
}); | ||
this.metrics.message = new prometheus.Counter({ | ||
name: "bridge_messages_total", | ||
help: "Total messages bridged into matrix, by type and protocol", | ||
labelNames: ["type", "protocol"], | ||
}); | ||
} | ||
@@ -219,2 +257,13 @@ | ||
if (this.config.metrics.enabled) { | ||
prometheus.collectDefaultMetrics(); | ||
const metricsServer = express(); | ||
metricsServer.get(this.config.metrics.path, async (req, res) => { | ||
res.set("Content-Type", prometheus.register.contentType); | ||
const metrics = await prometheus.register.metrics(); | ||
res.send(metrics); | ||
}); | ||
metricsServer.listen(this.config.metrics.port); | ||
} | ||
// pipe matrix-bot-sdk logging int ours | ||
@@ -395,2 +444,3 @@ const logMap = new Map<string, Log>(); | ||
const puppets = await this.provisioner.getAll(); | ||
this.metrics.puppet.set({protocol: this.protocol.id}, puppets.length); | ||
for (const p of puppets) { | ||
@@ -523,5 +573,9 @@ this.emit("puppetNew", p.puppetId, p.data); | ||
const room = await this.namespaceHandler.createRoom(roomData); | ||
if (!room || room.isDirect) { | ||
if (!room) { | ||
return; | ||
} | ||
this.metrics.room.inc({type: room.isDirect ? "dm" : "group", protocol: this.protocol?.id}); | ||
if (room.isDirect) { | ||
return; | ||
} | ||
log.info(`Got request to bridge room puppetId=${room.puppetId} roomId=${room.roomId}`); | ||
@@ -548,2 +602,3 @@ await this.roomSync.getMxid(room); | ||
await this.roomSync.delete(room, true); | ||
this.metrics.room.dec({type: room.isDirect ? "dm" : "group", protocol: this.protocol?.id}); | ||
} | ||
@@ -683,6 +738,24 @@ | ||
public trackConnectionStatus(puppetId: number, isConnected: boolean) { | ||
if (Boolean(this.connectionMetricStatus[puppetId]) === isConnected) { | ||
return; | ||
} | ||
this.connectionMetricStatus[puppetId] = isConnected; | ||
if (isConnected) { | ||
this.metrics.connected.inc({protocol: this.protocol.id}); | ||
} else { | ||
this.metrics.connected.dec({protocol: this.protocol.id}); | ||
} | ||
} | ||
/** | ||
* Send a status message either to the status message room or to a specified room | ||
*/ | ||
public async sendStatusMessage(puppetId: number | IRemoteRoom, msg: string) { | ||
public async sendStatusMessage(puppetId: number | IRemoteRoom, msg: string, isConnected: boolean | null = null) { | ||
if (isConnected !== null) { | ||
this.trackConnectionStatus( | ||
typeof puppetId === "number" ? puppetId : puppetId.puppetId, | ||
isConnected, | ||
); | ||
} | ||
await this.botProvisioner.sendStatusMessage(puppetId, msg); | ||
@@ -710,2 +783,3 @@ } | ||
await this.remoteEventHandler.sendFileByType("m.file", params, thing, name); | ||
this.metrics.message.inc({type: "file", protocol: this.protocol.id}); | ||
} | ||
@@ -718,2 +792,3 @@ | ||
await this.remoteEventHandler.sendFileByType("m.video", params, thing, name); | ||
this.metrics.message.inc({type: "video", protocol: this.protocol.id}); | ||
} | ||
@@ -726,2 +801,3 @@ | ||
await this.remoteEventHandler.sendFileByType("m.audio", params, thing, name); | ||
this.metrics.message.inc({type: "audio", protocol: this.protocol.id}); | ||
} | ||
@@ -734,2 +810,3 @@ | ||
await this.remoteEventHandler.sendFileByType("m.image", params, thing, name); | ||
this.metrics.message.inc({type: "image", protocol: this.protocol.id}); | ||
} | ||
@@ -742,2 +819,3 @@ | ||
await this.remoteEventHandler.sendMessage(params, opts); | ||
this.metrics.message.inc({type: "text", protocol: this.protocol.id}); | ||
} | ||
@@ -750,2 +828,3 @@ | ||
await this.remoteEventHandler.sendEdit(params, eventId, opts, ix); | ||
this.metrics.message.inc({type: "edit", protocol: this.protocol.id}); | ||
} | ||
@@ -758,2 +837,3 @@ | ||
await this.remoteEventHandler.sendRedact(params, eventId); | ||
this.metrics.message.inc({type: "redact", protocol: this.protocol.id}); | ||
} | ||
@@ -773,2 +853,3 @@ | ||
await this.remoteEventHandler.sendReaction(params, eventId, reaction); | ||
this.metrics.message.inc({type: "reaction", protocol: this.protocol.id}); | ||
} | ||
@@ -775,0 +856,0 @@ |
@@ -25,2 +25,3 @@ /* | ||
import * as unescapeHtml from "unescape"; | ||
import * as prometheus from "prom-client"; | ||
import { encode as blurhashEncode } from "blurhash"; | ||
@@ -48,2 +49,10 @@ import * as Canvas from "canvas"; | ||
this.ghostInviteCache = new TimedCache(PUPPET_INVITE_CACHE_LIFETIME); | ||
this.bridge.metrics.remoteUpdateBucket = new prometheus.Histogram({ | ||
name: "bridge_remote_update_seconds", | ||
help: "Time spent processing updates from the remote network, by protocol and type", | ||
labelNames: ["protocol", "type"], | ||
// tslint:disable-next-line no-magic-numbers | ||
buckets: [0.002, 0.005, 0.01, 0.25, 0.5, 0.75, 1, 1.5, 2, 3, 5, 7, 10], | ||
}); | ||
} | ||
@@ -150,2 +159,5 @@ | ||
} | ||
const stopTimer = this.bridge.metrics.remoteUpdateBucket.startTimer({ | ||
protocol: this.bridge.protocol.id, | ||
}); | ||
log.info(`Received message from ${params.user.userId} to send to ${params.room.roomId}`); | ||
@@ -178,2 +190,3 @@ this.preprocessMessageEvent(opts); | ||
await this.bridge.typingHandler.set(await client.getUserId(), mxid, false); | ||
stopTimer({ type: msgtype }); | ||
} | ||
@@ -185,2 +198,5 @@ | ||
} | ||
const stopTimer = this.bridge.metrics.remoteUpdateBucket.startTimer({ | ||
protocol: this.bridge.protocol.id, | ||
}); | ||
log.info(`Received edit from ${params.user.userId} to send to ${params.room.roomId}`); | ||
@@ -239,2 +255,3 @@ this.preprocessMessageEvent(opts); | ||
await this.bridge.typingHandler.set(await client.getUserId(), mxid, false); | ||
stopTimer({ type: msgtype }); | ||
} | ||
@@ -258,2 +275,5 @@ | ||
} | ||
const stopTimer = this.bridge.metrics.remoteUpdateBucket.startTimer({ | ||
protocol: this.bridge.protocol.id, | ||
}); | ||
log.info(`Received reply from ${params.user.userId} to send to ${params.room.roomId}`); | ||
@@ -295,4 +315,4 @@ this.preprocessMessageEvent(opts); | ||
} | ||
const bodyParts = info.message.body.split("\n"); | ||
bodyParts[0] = `${info.message.emote ? "* " : ""}<${info.user.mxid}> ${bodyParts[0]}`; | ||
const bodyParts = this.preprocessBody(info.message.body).split("\n"); | ||
bodyParts[0] = `${info.message.emote ? "* " : ""}<${this.preprocessBody(info.user.mxid)}> ${bodyParts[0]}`; | ||
send.body = `${bodyParts.map((l) => `> ${l}`).join("\n")}\n\n${send.body}`; | ||
@@ -318,3 +338,3 @@ const matrixReplyRegex = /^<mx-reply>.*<\/mx-reply>/gs; | ||
} | ||
const plainHeader = `> <${info.user.mxid}> sent ${msg}.\n\n`; | ||
const plainHeader = `> <${this.preprocessBody(info.user.mxid)}> sent ${msg}.\n\n`; | ||
send.body = plainHeader + send.body; | ||
@@ -344,2 +364,3 @@ const richHeader = `<mx-reply><blockquote> | ||
await this.bridge.typingHandler.set(await client.getUserId(), mxid, false); | ||
stopTimer({ type: msgtype }); | ||
} | ||
@@ -381,2 +402,5 @@ | ||
} | ||
const stopTimer = this.bridge.metrics.remoteUpdateBucket.startTimer({ | ||
protocol: this.bridge.protocol.id, | ||
}); | ||
log.info(`Received file to send from ${params.user.userId} in ${params.room.roomId}.`); | ||
@@ -533,2 +557,3 @@ log.verbose(`thing=${typeof thing === "string" ? thing : "<Buffer>"} name=${name}`); | ||
await this.bridge.typingHandler.set(await client.getUserId(), mxid, false); | ||
stopTimer({ type: msgtype }); | ||
} | ||
@@ -625,7 +650,12 @@ | ||
private preprocessMessageEvent(opts: IMessageEvent) { | ||
private preprocessBody(body: string): string { | ||
for (const homeserver of this.bridge.config.bridge.stripHomeservers) { | ||
const urlRegex = homeserver.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); | ||
opts.body = opts.body.replace(new RegExp(`@([\x21-\x39\x3b-\x7e]+):${urlRegex}`, "g"), "@$1"); | ||
body = body.replace(new RegExp(`@([\x21-\x39\x3b-\x7e]+):${urlRegex}`, "g"), "@$1"); | ||
} | ||
return body; | ||
} | ||
private preprocessMessageEvent(opts: IMessageEvent) { | ||
opts.body = this.preprocessBody(opts.body); | ||
if (!opts.formattedBody) { | ||
@@ -632,0 +662,0 @@ return; |
@@ -54,2 +54,24 @@ /* | ||
this.mxidLock = new Lock(MXID_LOOKUP_LOCK_TIMEOUT); | ||
if (this.bridge.config.metrics.enabled) { | ||
const roomMetricsInit = (rooms) => { | ||
this.bridge.metrics.room.set( | ||
{ | ||
type: "dm", | ||
protocol: this.bridge.protocol.id, | ||
}, | ||
rooms.filter((room) => room.isDirect).length, | ||
); | ||
this.bridge.metrics.room.set( | ||
{ | ||
type: "group", | ||
protocol: this.bridge.protocol.id, | ||
}, | ||
rooms.filter((room) => !room.isDirect).length, | ||
); | ||
}; | ||
this.roomStore.getAll() | ||
.catch((err) => log.error("could not get room store")) | ||
.then(roomMetricsInit) | ||
.catch((err) => log.warn("could not init room metrics")); | ||
} | ||
} | ||
@@ -56,0 +78,0 @@ |
@@ -206,9 +206,9 @@ /* | ||
this.db.Open(); | ||
this.pRoomStore = new DbRoomStore(this.db); | ||
this.pUserStore = new DbUserStore(this.db); | ||
this.pGroupStore = new DbGroupStore(this.db); | ||
this.pPuppetStore = new DbPuppetStore(this.db); | ||
this.pEventStore = new DbEventStore(this.db); | ||
this.pReactionStore = new DbReactionStore(this.db); | ||
this.pEmoteStore = new DbEmoteStore(this.db); | ||
this.pRoomStore = new DbRoomStore(this.db, undefined, this.bridge.protocol?.id); | ||
this.pUserStore = new DbUserStore(this.db, undefined, this.bridge.protocol?.id); | ||
this.pGroupStore = new DbGroupStore(this.db, undefined, this.bridge.protocol?.id); | ||
this.pPuppetStore = new DbPuppetStore(this.db, undefined, this.bridge.protocol?.id); | ||
this.pEventStore = new DbEventStore(this.db, this.bridge.protocol?.id); | ||
this.pReactionStore = new DbReactionStore(this.db, this.bridge.protocol?.id); | ||
this.pEmoteStore = new DbEmoteStore(this.db, this.bridge.protocol?.id); | ||
} catch (ex) { | ||
@@ -215,0 +215,0 @@ log.error("Error opening database:", ex); |
@@ -24,2 +24,3 @@ /* | ||
import { StringFormatter } from "./structures/stringformatter"; | ||
import * as prometheus from "prom-client"; | ||
@@ -44,2 +45,12 @@ const log = new Log("UserSync"); | ||
this.roomOverrideLock = new Lock(ROOM_OVERRIDE_LOCK_TIMEOUT); | ||
const that = this; | ||
this.bridge.metrics.remoteUser = new prometheus.Gauge({ | ||
name: "bridge_remote_users_total", | ||
help: "Total number of users on the remote network", | ||
labelNames: ["protocol"], | ||
async collect() { | ||
const remoteUsers = await that.userStore.getAll(); | ||
this.set({protocol: that.bridge.protocol.id}, remoteUsers.length); | ||
}, | ||
}); | ||
} | ||
@@ -46,0 +57,0 @@ |
928064
23527
18
+ Addedprom-client@^13.0.0
+ Addedbintrees@1.0.2(transitive)
+ Addedprom-client@13.2.0(transitive)
+ Addedtdigest@0.1.2(transitive)