mirror of
https://github.com/turt2live/matrix-dimension.git
synced 2024-10-01 01:05:53 -04:00
Use async/await throughout the backend
This commit is contained in:
parent
8d6c2dfa00
commit
7c757a79e6
@ -26,11 +26,14 @@ export class ScalarService {
|
||||
const cachedUserId = Cache.for(CACHE_SCALAR_ACCOUNTS).get(scalarToken);
|
||||
if (cachedUserId) return cachedUserId;
|
||||
|
||||
try {
|
||||
const user = await ScalarStore.getTokenOwner(scalarToken, ignoreUpstreams);
|
||||
if (!user) throw new ApiError(401, "Invalid token");
|
||||
|
||||
Cache.for(CACHE_SCALAR_ACCOUNTS).put(scalarToken, user.userId, 30 * 60 * 1000); // 30 minutes
|
||||
return user.userId;
|
||||
} catch (err) {
|
||||
LogService.error("ScalarService", err);
|
||||
throw new ApiError(401, "Invalid token");
|
||||
}
|
||||
}
|
||||
|
||||
@POST
|
||||
|
@ -2,7 +2,6 @@ import AppService from "./models/AppService";
|
||||
import AppServiceUser from "./models/AppServiceUser";
|
||||
import * as randomString from "random-string";
|
||||
import { MatrixAppserviceClient } from "../matrix/MatrixAppserviceClient";
|
||||
import { resolveIfExists } from "./DimensionStore";
|
||||
import config from "../config";
|
||||
|
||||
export class AppserviceStore {
|
||||
@ -21,11 +20,15 @@ export class AppserviceStore {
|
||||
}
|
||||
|
||||
public static async getUser(appserviceId: string, userId: string): Promise<AppServiceUser> {
|
||||
return AppServiceUser.findOne({where: {appserviceId: appserviceId, id: userId}}).then(resolveIfExists);
|
||||
const user = await AppServiceUser.findOne({where: {appserviceId: appserviceId, id: userId}});
|
||||
if (!user) throw new Error("User not found");
|
||||
return user;
|
||||
}
|
||||
|
||||
public static async getByHomeserverToken(hsToken: string): Promise<AppService> {
|
||||
return AppService.findOne({where: {hsToken: hsToken}}).then(resolveIfExists);
|
||||
const appservice = AppService.findOne({where: {hsToken: hsToken}});
|
||||
if (!appservice) throw new Error("Appservice not found");
|
||||
return appservice;
|
||||
}
|
||||
|
||||
public static async getAllByUserPrefix(userPrefix: string): Promise<AppService[]> {
|
||||
@ -44,17 +47,19 @@ export class AppserviceStore {
|
||||
|
||||
public static async registerUser(appserviceId: string, userId: string): Promise<AppServiceUser> {
|
||||
userId = AppserviceStore.getSafeUserId(userId);
|
||||
return AppService.findOne({where: {id: appserviceId}}).then(resolveIfExists).then(appservice => {
|
||||
|
||||
const appservice = await AppService.findOne({where: {id: appserviceId}});
|
||||
if (!appservice) throw new Error("Appservice not found");
|
||||
|
||||
const client = new MatrixAppserviceClient(config.homeserver.name, appservice);
|
||||
const localpart = userId.substring(1).split(":")[0];
|
||||
return client.registerUser(localpart);
|
||||
}).then(response => {
|
||||
const response = await client.registerUser(localpart);
|
||||
|
||||
return AppServiceUser.create({
|
||||
id: userId,
|
||||
appserviceId: appserviceId,
|
||||
accessToken: response.access_token,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private constructor() {
|
||||
|
@ -57,8 +57,3 @@ class _DimensionStore {
|
||||
}
|
||||
|
||||
export const DimensionStore = new _DimensionStore();
|
||||
|
||||
export function resolveIfExists<T>(record: T): Promise<T> {
|
||||
if (!record) return Promise.reject("Record not found");
|
||||
return Promise.resolve(record);
|
||||
}
|
@ -1,4 +1,3 @@
|
||||
import { resolveIfExists } from "./DimensionStore";
|
||||
import { NebConfig } from "../models/neb";
|
||||
import NebConfiguration from "./models/NebConfiguration";
|
||||
import NebIntegration from "./models/NebIntegration";
|
||||
@ -77,53 +76,53 @@ export class NebStore {
|
||||
};
|
||||
|
||||
public static async getAllConfigs(): Promise<NebConfig[]> {
|
||||
return NebConfiguration.findAll().then(configs => {
|
||||
const configs = await NebConfiguration.findAll();
|
||||
return Promise.all((configs || []).map(c => NebStore.getConfig(c.id)));
|
||||
});
|
||||
}
|
||||
|
||||
public static async getConfig(id: number): Promise<NebConfig> {
|
||||
let nebConfig: NebConfiguration;
|
||||
return NebConfiguration.findByPrimary(id).then(resolveIfExists).then(conf => {
|
||||
nebConfig = conf;
|
||||
return NebIntegration.findAll({where: {nebId: id}});
|
||||
}).then(integrations => {
|
||||
return NebStore.getCompleteIntegrations(nebConfig, integrations);
|
||||
}).then(integrations => {
|
||||
return new NebConfig(nebConfig, integrations);
|
||||
});
|
||||
const config = await NebConfiguration.findByPrimary(id);
|
||||
if (!config) throw new Error("Configuration not found");
|
||||
|
||||
const integrations = await NebIntegration.findAll({where: {nebId: id}});
|
||||
const fullIntegrations = await NebStore.getCompleteIntegrations(config, integrations);
|
||||
|
||||
return new NebConfig(config, fullIntegrations);
|
||||
}
|
||||
|
||||
public static async createForUpstream(upstreamId: number): Promise<NebConfig> {
|
||||
return Upstream.findByPrimary(upstreamId).then(resolveIfExists).then(upstream => {
|
||||
return NebConfiguration.create({
|
||||
const upstream = await Upstream.findByPrimary(upstreamId);
|
||||
if (!upstream) throw new Error("Upstream not found");
|
||||
|
||||
const config = await NebConfiguration.create({
|
||||
upstreamId: upstream.id,
|
||||
});
|
||||
}).then(config => {
|
||||
|
||||
return NebStore.getConfig(config.id);
|
||||
});
|
||||
}
|
||||
|
||||
public static async createForAppservice(appserviceId: string, adminUrl: string): Promise<NebConfig> {
|
||||
return AppService.findByPrimary(appserviceId).then(resolveIfExists).then(appservice => {
|
||||
return NebConfiguration.create({
|
||||
const appservice = await AppService.findByPrimary(appserviceId);
|
||||
if (!appservice) throw new Error("Appservice not found");
|
||||
|
||||
const config = await NebConfiguration.create({
|
||||
appserviceId: appservice.id,
|
||||
adminUrl: adminUrl,
|
||||
});
|
||||
}).then(config => {
|
||||
|
||||
return NebStore.getConfig(config.id);
|
||||
});
|
||||
}
|
||||
|
||||
public static async getOrCreateIntegration(configurationId: number, integrationType: string): Promise<NebIntegration> {
|
||||
if (!NebStore.INTEGRATIONS[integrationType]) return Promise.reject(new Error("Integration not supported"));
|
||||
if (!NebStore.INTEGRATIONS[integrationType]) throw new Error("Integration not supported");
|
||||
|
||||
return NebConfiguration.findByPrimary(configurationId).then(resolveIfExists).then(config => {
|
||||
return NebIntegration.findOne({where: {nebId: config.id, type: integrationType}});
|
||||
}).then(integration => {
|
||||
const config = await NebConfiguration.findByPrimary(configurationId);
|
||||
if (!config) throw new Error("Configuration not found");
|
||||
|
||||
let integration = await NebIntegration.findOne({where: {nebId: config.id, type: integrationType}});
|
||||
if (!integration) {
|
||||
LogService.info("NebStore", "Creating integration " + integrationType + " for NEB " + configurationId);
|
||||
return NebIntegration.create({
|
||||
integration = await NebIntegration.create({
|
||||
type: integrationType,
|
||||
name: NebStore.INTEGRATIONS[integrationType].name,
|
||||
avatarUrl: NebStore.INTEGRATIONS[integrationType].avatarUrl,
|
||||
@ -132,11 +131,12 @@ export class NebStore {
|
||||
isPublic: true,
|
||||
nebId: configurationId,
|
||||
});
|
||||
} else return Promise.resolve(integration);
|
||||
});
|
||||
}
|
||||
|
||||
public static getCompleteIntegrations(nebConfig: NebConfiguration, knownIntegrations: NebIntegration[]): Promise<NebIntegration[]> {
|
||||
return integration;
|
||||
}
|
||||
|
||||
public static async getCompleteIntegrations(nebConfig: NebConfiguration, knownIntegrations: NebIntegration[]): Promise<NebIntegration[]> {
|
||||
const supported = NebStore.getSupportedIntegrations(nebConfig);
|
||||
const notSupported: SupportedIntegration[] = [];
|
||||
for (const supportedIntegration of supported) {
|
||||
|
@ -6,48 +6,41 @@ import User from "./models/User";
|
||||
export class ScalarStore {
|
||||
|
||||
public static async doesUserHaveTokensForAllUpstreams(userId: string): Promise<boolean> {
|
||||
let upstreamTokenIds: number[] = [];
|
||||
let hasDimensionToken = false;
|
||||
return UserScalarToken.findAll({where: {userId: userId}}).then(results => {
|
||||
upstreamTokenIds = results.filter(t => !t.isDimensionToken).map(t => t.upstreamId);
|
||||
hasDimensionToken = results.filter(t => t.isDimensionToken).length >= 1;
|
||||
return Upstream.findAll();
|
||||
}).then(upstreams => {
|
||||
const scalarTokens = await UserScalarToken.findAll({where: {userId: userId}});
|
||||
const upstreamTokenIds = scalarTokens.filter(t => !t.isDimensionToken).map(t => t.upstreamId);
|
||||
const hasDimensionToken = scalarTokens.filter(t => t.isDimensionToken).length >= 1;
|
||||
|
||||
if (!hasDimensionToken) {
|
||||
LogService.warn("DimensionStore", "User " + userId + " is missing a Dimension scalar token");
|
||||
LogService.warn("ScalarStore", "User " + userId + " is missing a Dimension scalar token");
|
||||
return false;
|
||||
}
|
||||
|
||||
const upstreams = await Upstream.findAll();
|
||||
for (const upstream of upstreams) {
|
||||
if (upstreamTokenIds.indexOf(upstream.id) === -1) {
|
||||
LogService.warn("DimensionStore", "User " + userId + " is missing a scalar token for upstream " + upstream.id + " (" + upstream.name + ")");
|
||||
LogService.warn("ScalarStore", "user " + userId + " is missing a scalar token for upstream " + upstream.id + " (" + upstream.name + ")");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
public static async getTokenOwner(scalarToken: string, ignoreUpstreams?: boolean): Promise<User> {
|
||||
let user: User = null;
|
||||
return UserScalarToken.findAll({
|
||||
const tokens = await UserScalarToken.findAll({
|
||||
where: {isDimensionToken: true, scalarToken: scalarToken},
|
||||
include: [User]
|
||||
}).then(tokens => {
|
||||
if (!tokens || tokens.length === 0) {
|
||||
return Promise.reject("Invalid token");
|
||||
}
|
||||
|
||||
user = tokens[0].user;
|
||||
if (ignoreUpstreams) return true; // they have all the upstreams as far as we're concerned
|
||||
return ScalarStore.doesUserHaveTokensForAllUpstreams(user.userId);
|
||||
}).then(hasUpstreams => {
|
||||
if (!hasUpstreams) {
|
||||
return Promise.reject("Invalid token"); // missing one or more upstreams == no validation
|
||||
}
|
||||
return Promise.resolve(user);
|
||||
});
|
||||
if (!tokens || tokens.length === 0) throw new Error("Invalid token");
|
||||
|
||||
const user = tokens[0].user;
|
||||
if (ignoreUpstreams) return user; // skip upstreams check
|
||||
|
||||
const hasAllTokens = await ScalarStore.doesUserHaveTokensForAllUpstreams(user.userId);
|
||||
if (!hasAllTokens) {
|
||||
throw new Error("Invalid token"); // They are missing an upstream, so we'll lie and say they are not authorized
|
||||
}
|
||||
return user;
|
||||
}
|
||||
|
||||
private constructor() {
|
||||
|
@ -1,28 +1,30 @@
|
||||
import WidgetRecord from "./models/WidgetRecord";
|
||||
import { Widget } from "../integrations/Widget";
|
||||
import { resolveIfExists } from "./DimensionStore";
|
||||
|
||||
export class WidgetStore {
|
||||
|
||||
public static async listAll(isEnabled?: boolean): Promise<Widget[]> {
|
||||
let conditions = {};
|
||||
if (isEnabled === true || isEnabled === false) conditions = {where: {isEnabled: isEnabled}};
|
||||
return WidgetRecord.findAll(conditions).then(widgets => widgets.map(w => new Widget(w)));
|
||||
return (await WidgetRecord.findAll(conditions)).map(w => new Widget(w));
|
||||
}
|
||||
|
||||
public static async setEnabled(type: string, isEnabled: boolean): Promise<any> {
|
||||
return WidgetRecord.findOne({where: {type: type}}).then(resolveIfExists).then(widget => {
|
||||
const widget = await WidgetRecord.findOne({where: {type: type}});
|
||||
if (!widget) throw new Error("Widget not found");
|
||||
|
||||
widget.isEnabled = isEnabled;
|
||||
return widget.save();
|
||||
});
|
||||
}
|
||||
|
||||
public static async setOptions(type: string, options: any): Promise<any> {
|
||||
const optionsJson = JSON.stringify(options);
|
||||
return WidgetRecord.findOne({where: {type: type}}).then(resolveIfExists).then(widget => {
|
||||
|
||||
const widget = await WidgetRecord.findOne({where: {type: type}});
|
||||
if (!widget) throw new Error("Widget not found");
|
||||
|
||||
widget.optionsJson = optionsJson;
|
||||
return widget.save();
|
||||
});
|
||||
return await widget.save();
|
||||
}
|
||||
|
||||
private constructor() {
|
||||
|
@ -13,7 +13,7 @@ export class MatrixAppserviceClient {
|
||||
constructor(private homeserverName: string, private appservice: AppService) {
|
||||
}
|
||||
|
||||
public registerUser(localpart: string): Promise<MatrixUserResponse> {
|
||||
public async registerUser(localpart: string): Promise<MatrixUserResponse> {
|
||||
return doFederatedApiCall(
|
||||
"POST",
|
||||
this.homeserverName,
|
||||
|
@ -10,29 +10,26 @@ export class MatrixLiteClient {
|
||||
constructor(private homeserverName: string, private accessToken: string) {
|
||||
}
|
||||
|
||||
public getFederationUrl(): Promise<string> {
|
||||
public async getFederationUrl(): Promise<string> {
|
||||
return getFedUrl(this.homeserverName);
|
||||
}
|
||||
|
||||
public getUrlPreview(url: string): Promise<MatrixUrlPreview> {
|
||||
public async getUrlPreview(url: string): Promise<MatrixUrlPreview> {
|
||||
return doFederatedApiCall(
|
||||
"GET",
|
||||
this.homeserverName,
|
||||
"/_matrix/media/r0/preview_url",
|
||||
{access_token: this.accessToken, url: url}
|
||||
).then(response => {
|
||||
return response;
|
||||
});
|
||||
);
|
||||
}
|
||||
|
||||
public whoAmI(): Promise<string> {
|
||||
return doFederatedApiCall(
|
||||
public async whoAmI(): Promise<string> {
|
||||
const response = await doFederatedApiCall(
|
||||
"GET",
|
||||
this.homeserverName,
|
||||
"/_matrix/client/r0/account/whoami",
|
||||
{access_token: this.accessToken}
|
||||
).then(response => {
|
||||
return response["user_id"];
|
||||
});
|
||||
);
|
||||
return response['user_id'];
|
||||
}
|
||||
}
|
||||
|
@ -6,14 +6,13 @@ export class MatrixOpenIdClient {
|
||||
constructor(private openId: OpenId) {
|
||||
}
|
||||
|
||||
public getUserId(): Promise<string> {
|
||||
return doFederatedApiCall(
|
||||
public async getUserId(): Promise<string> {
|
||||
const response = await doFederatedApiCall(
|
||||
"GET",
|
||||
this.openId.matrix_server_name,
|
||||
"/_matrix/federation/v1/openid/userinfo",
|
||||
{access_token: this.openId.access_token}
|
||||
).then(response => {
|
||||
);
|
||||
return response['sub'];
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -3,37 +3,39 @@ import { LogService } from "matrix-js-snippets";
|
||||
import { Cache, CACHE_FEDERATION } from "../MemoryCache";
|
||||
import * as request from "request";
|
||||
|
||||
export function getFederationUrl(serverName: string): Promise<string> {
|
||||
export async function getFederationUrl(serverName: string): Promise<string> {
|
||||
const cachedUrl = Cache.for(CACHE_FEDERATION).get(serverName);
|
||||
if (cachedUrl) {
|
||||
LogService.verbose("matrix", "Cached federation URL for " + serverName + " is " + cachedUrl);
|
||||
return Promise.resolve(cachedUrl);
|
||||
return cachedUrl;
|
||||
}
|
||||
|
||||
let serverUrl = null;
|
||||
let expirationMs = 4 * 60 * 60 * 1000; // default is 4 hours
|
||||
const dnsPromise = dns.resolveSrv("_matrix._tcp." + serverName);
|
||||
return Promise.resolve(dnsPromise).then(records => {
|
||||
|
||||
try {
|
||||
const records = await dns.resolveSrv("_matrix._tcp." + serverName);
|
||||
if (records && records.length > 0) {
|
||||
serverUrl = "https://" + records[0].name + ":" + records[0].port;
|
||||
expirationMs = records[0].ttl * 1000;
|
||||
}
|
||||
}, _err => {
|
||||
} catch (err) {
|
||||
// Not having the SRV record isn't bad, it just means that the server operator decided to not use SRV records.
|
||||
// When there's no SRV record we default to port 8448 (as per the federation rules) in the lower .then()
|
||||
// People tend to think that the lack of an SRV record is bad, but in reality it's only a problem if one was set and
|
||||
// it's not being found. Most people don't set up the SRV record, but some do.
|
||||
LogService.verbose("matrix", err);
|
||||
LogService.warn("matrix", "Could not find _matrix._tcp." + serverName + " DNS record. This is normal for most servers.");
|
||||
}).then(() => {
|
||||
}
|
||||
|
||||
if (!serverUrl) serverUrl = "https://" + serverName + ":8448";
|
||||
LogService.verbose("matrix", "Federation URL for " + serverName + " is " + serverUrl + " - caching for " + expirationMs + " ms");
|
||||
Cache.for(CACHE_FEDERATION).put(serverName, serverUrl, expirationMs);
|
||||
return serverUrl;
|
||||
});
|
||||
}
|
||||
|
||||
export function doFederatedApiCall(method: string, serverName: string, endpoint: string, query?: object, body?: object): Promise<any> {
|
||||
return getFederationUrl(serverName).then(federationUrl => {
|
||||
export async function doFederatedApiCall(method: string, serverName: string, endpoint: string, query?: object, body?: object): Promise<any> {
|
||||
const federationUrl = await getFederationUrl(serverName);
|
||||
return new Promise((resolve, reject) => {
|
||||
request({
|
||||
method: method,
|
||||
@ -55,5 +57,4 @@ export function doFederatedApiCall(method: string, serverName: string, endpoint:
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
Loading…
Reference in New Issue
Block a user