import { Server } from "./server" import Script from "./Script" import { OfflineBasemapManager } from "../src/Logic/OfflineBasemapManager" import http from "node:http" import { ServerResponse } from "http" import { existsSync, statSync, writeFileSync } from "fs" import ScriptUtils from "./ScriptUtils" import { PmTilesExtractGenerator } from "./pmTilesExtractGenerator" class ServerPmTileExtracts extends Script { private static FILE_IS_STALE_AFTER_SEC = 30 * 24 * 60 * 60 constructor() { super( "Starts a server that serves PMtiles. Usage:\n" + "sourceFile cachedir [portnumber??2346]" ) } async main(args: string[]): Promise { if (args.length < 2) { this.printHelp() throw "!!! Please, specify a source- and cachedir !!!" } const sourcefile = args[0] const targetDir = args[1] const port = Number(args[2] ?? "2346") const zoomlevels = OfflineBasemapManager.zoomelevels const generator = new PmTilesExtractGenerator(sourcefile, targetDir) const creationDates = new Map() new Server(port, {}, [ { mustMatch: /\d+\/\d+\/\d+.pmtiles/, unmanaged: true, mimetype: "application/octet-stream", handle: async ( path: string, queryParams: URLSearchParams, req: http.IncomingMessage, body: string, res: ServerResponse ) => { const [z, x, y] = path .split(".")[0] .split("/") .map((x) => Number(x)) if (!(z in zoomlevels)) { throw `Invalid zoomlevel ${z} (x: ${x}, y: ${y}, must be one of ${Array.from( Object.keys(zoomlevels) ).join(", ")}` } const targetFile = generator.getFilename(z, x, y) let targetCreationDate = creationDates.get(targetFile) let isStale = targetCreationDate === undefined ? false : new Date().getTime() - targetCreationDate.getTime() > 1000 * ServerPmTileExtracts.FILE_IS_STALE_AFTER_SEC if (isStale || !existsSync(targetFile)) { ScriptUtils.createParentDir(targetFile) console.log("Creating", targetFile) const start = new Date() const maxzoom = OfflineBasemapManager.zoomelevels[z] await generator.generateArchive(z, x, y, maxzoom) const stop = new Date() console.log( "Creating ", targetFile, "took", stop.getTime() - start.getTime() + "ms" ) } else if (targetCreationDate === undefined) { const stats = statSync(targetFile) creationDates.set(targetFile, stats.mtime) } if (req.destroyed) { return null } const stats = statSync(targetFile) if(stats.size >= 250 * 1000000 /* Megabyte*/){ throw "File to big to transfer, over 100 MB" } res.writeHead(200, { "Content-Type": "application/octet-stream" , "Content-Length": stats.size }) Server.sendFile(targetFile, res) return null }, }, ]) } } new ServerPmTileExtracts().run()