@versatiles/google-cloud
Version:
A server for VersaTiles in Google Cloud Run
101 lines (100 loc) • 3.96 kB
JavaScript
import express from 'express';
import { Responder } from './responder.js';
import { BucketGoogle, BucketLocal } from './bucket/index.js';
import { getVersatiles } from './versatiles/index.js';
import { readFileSync } from 'fs';
/**
* Starts an Express server with specified options.
* @param opt - Configuration options for the server.
* @returns A promise resolving to the Express server instance.
*/
export async function startServer(opt) {
const { port, fastRecompression, rewriteRules, verbose } = opt;
let bucketPrefix = opt.bucketPrefix.replace(/^\/+|\/+$/g, '');
if (bucketPrefix !== '')
bucketPrefix += '/';
const baseUrl = new URL(opt.baseUrl).href;
// Initialize the bucket based on the provided options
let bucket;
if (typeof opt.localDirectory == 'string') {
bucket = new BucketLocal(opt.localDirectory);
}
else if (typeof opt.bucket == 'string') {
bucket = new BucketGoogle(opt.bucket);
}
else {
bucket = opt.bucket;
}
await bucket.check();
let requestNo = 0;
const app = express();
app.set('query parser', (a) => a);
app.disable('x-powered-by');
// Health check endpoint
app.get('/healthcheck', (serverRequest, serverResponse) => {
serverResponse
.status(200)
.type('text')
.send('ok');
});
// Handler for all GET requests
app.get(/.*/, (request, response) => {
void (async () => {
requestNo++;
const responder = new Responder({
fastRecompression,
requestHeaders: request.headers,
requestNo,
response,
verbose,
});
let { url } = request;
responder.log('new request: ' + url);
try {
// handle rewrite rules
for (const rewriteRule of rewriteRules) {
if (url.startsWith(rewriteRule[0])) {
responder.log('use rewrite rule: ' + rewriteRule.join(' => '));
url = url.replace(...rewriteRule);
responder.log('new url: ' + url);
}
}
const { pathname, search } = new URL(url, 'http://a.b');
const filename = pathname.replace(/^\/+|:/g, '');
responder.log(`request file: ${bucketPrefix + filename}`);
const file = bucket.getFile(bucketPrefix + filename);
if (filename.endsWith('.versatiles')) {
const container = await getVersatiles(file, baseUrl + filename);
await container.serve(search, responder);
}
else {
await file.serve(responder);
}
}
catch (error) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
switch (error.code) {
case 'ENOENT':
case 404:
responder.error(404, `file "${url}" not found`);
return;
}
console.error(error);
responder.error(500, 'Internal Server Error for request: ' + JSON.stringify(request.path));
}
})();
});
// Start the server and return the server instance
return new Promise((res, rej) => {
const server = app.listen(port, () => {
const { version } = JSON.parse(readFileSync(new URL('../../package.json', import.meta.url), 'utf8'));
console.log(`starting server @versatiles/google-cloud v${version}`);
console.log(`listening on port ${port}`);
console.log(`you can find me at ${baseUrl}`);
res(server);
}).on('error', error => {
console.log(`server error: ${error.message}`);
rej(error);
});
});
}