matrix-public-archive/server/routes/room-directory-routes.js

124 lines
4.1 KiB
JavaScript

'use strict';
const assert = require('assert');
const path = require('path');
const urlJoin = require('url-join');
const express = require('express');
const asyncHandler = require('../lib/express-async-handler');
const RouteTimeoutAbortError = require('../lib/errors/route-timeout-abort-error');
const UserClosedConnectionAbortError = require('../lib/errors/user-closed-connection-abort-error');
const identifyRoute = require('../middleware/identify-route-middleware');
const fetchPublicRooms = require('../lib/matrix-utils/fetch-public-rooms');
const renderHydrogenVmRenderScriptToPageHtml = require('../hydrogen-render/render-hydrogen-vm-render-script-to-page-html');
const setHeadersToPreloadAssets = require('../lib/set-headers-to-preload-assets');
const config = require('../lib/config');
const basePath = config.get('basePath');
assert(basePath);
const matrixServerUrl = config.get('matrixServerUrl');
assert(matrixServerUrl);
const matrixServerName = config.get('matrixServerName');
assert(matrixServerName);
const matrixAccessToken = config.get('matrixAccessToken');
assert(matrixAccessToken);
const stopSearchEngineIndexing = config.get('stopSearchEngineIndexing');
const router = express.Router({
caseSensitive: true,
// Preserve the req.params values from the parent router.
mergeParams: true,
});
router.get(
'/',
identifyRoute('app-room-directory-index'),
asyncHandler(async function (req, res) {
const paginationToken = req.query.page;
const searchTerm = req.query.search;
const homeserver = req.query.homeserver;
// It would be good to grab more rooms than we display in case we need
// to filter any out but then the pagination tokens with the homeserver
// will be out of sync. XXX: It would be better if we could just filter
// `/publicRooms` directly via the API (needs MSC).
const limit = 9;
let rooms = [];
let nextPaginationToken;
let prevPaginationToken;
let roomFetchError;
try {
({ rooms, nextPaginationToken, prevPaginationToken } = await fetchPublicRooms(
matrixAccessToken,
{
server: homeserver,
searchTerm,
paginationToken,
limit,
abortSignal: req.abortSignal,
}
));
} catch (err) {
if (err instanceof RouteTimeoutAbortError || err instanceof UserClosedConnectionAbortError) {
// Throw an error so we stop processing and assembling the page after we abort
throw err;
} else {
// Otherwise, this will be the error we will display on the page for the user to
// explain why we failed to fetch the rooms they wanted.
roomFetchError = err;
}
}
// We index the room directory unless the config says we shouldn't index anything
const shouldIndex = !stopSearchEngineIndexing;
const pageOptions = {
title: `Matrix Public Archive`,
description:
'Browse thousands of rooms using Matrix. The new portal into the Matrix ecosystem.',
entryPoint: 'client/js/entry-client-room-directory.js',
locationUrl: urlJoin(basePath, req.originalUrl),
shouldIndex,
cspNonce: res.locals.cspNonce,
};
const pageHtml = await renderHydrogenVmRenderScriptToPageHtml({
pageOptions,
vmRenderScriptFilePath: path.resolve(
__dirname,
'../../shared/room-directory-vm-render-script.js'
),
vmRenderContext: {
rooms,
roomFetchError: roomFetchError
? {
message: roomFetchError.message,
stack: roomFetchError.stack,
}
: null,
nextPaginationToken,
prevPaginationToken,
pageSearchParameters: {
homeserver: homeserver || matrixServerName,
searchTerm,
paginationToken,
limit,
},
config: {
basePath,
matrixServerUrl,
matrixServerName,
},
},
abortSignal: req.abortSignal,
});
setHeadersToPreloadAssets(res, pageOptions);
res.set('Content-Type', 'text/html');
res.send(pageHtml);
})
);
module.exports = router;