tileblaster 0.4.9 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/tileblaster.js ADDED
@@ -0,0 +1,469 @@
1
+ const http = require("node:http");
2
+ const path = require("node:path");
3
+ const fs = require("node:fs");
4
+ const os = require("node:os");
5
+
6
+ const decompress = require("./lib/decompress");
7
+ const retrieve = require("./lib/retrieve");
8
+ const router = require("./lib/router");
9
+ const strtpl = require("./lib/strtpl");
10
+ const debug = require("./lib/debug");
11
+ const store = require("./lib/store");
12
+ const tasks = require("./lib/tasks");
13
+ const load = require("./lib/load");
14
+ const mime = require("./lib/mime");
15
+
16
+ const quu = require("quu");
17
+ const sharp = load("sharp"); // optional dep
18
+
19
+ const tileblaster = module.exports = function tileblaster(config){
20
+ if (!(this instanceof tileblaster)) return new tileblaster(...arguments);
21
+ const self = this;
22
+
23
+ // configure
24
+ self.config = {};
25
+ self.configure(config);
26
+
27
+ // libraries (expose to plugins)
28
+ self.lib = { retrieve, debug, load, tasks, mime, store, strtpl, decompress, sharp };
29
+
30
+ // plugins
31
+ self.plugins = {};
32
+ self.loadPlugins(self.config.plugins);
33
+
34
+ // load builtins
35
+ self.builtins = {};
36
+ self.loadBuiltins([ "cors", "parse", "check", "noop", "tileserver", "versatiles", "pmtiles", "mbtiles", "edit", "compress", "cache", "deliver", "dump", "modernize", "optimize", "sharp" ]);
37
+
38
+ // assemble task lists for maps
39
+ self.maps = {};
40
+ self.prepareMaps();
41
+
42
+ self.queue = quu(self.config.queue);
43
+
44
+ // router
45
+ self.router = router({ mountpoint: self.config.server.mount });
46
+
47
+ // default route
48
+ self.router.default(function(req, res){
49
+ res.statusCode = 404;
50
+ res.setHeader("content-type", "text/plain");
51
+ res.end("not found.");
52
+ });
53
+
54
+ // index route
55
+ self.router.route("/", function(req, res){
56
+ res.statusCode = 200;
57
+ /*
58
+ res.setHeader("content-type", "text/plain");
59
+ res.end("tileblaster ready.");
60
+ */
61
+
62
+ // fancy index page
63
+ res.setHeader("content-type", "text/html");
64
+ res.end('<!doctype html><html lang="en"><head><meta charset="utf-8"><title>tileblaster</title><style>body{display: flex;height: 100vh;width: 100vw;margin: 0;padding: 0;justify-content: center;align-items: center;background: rgb(122,32,168);background: radial-gradient(circle, rgba(122,32,168,1) 8%, rgba(252,70,167,1) 100%);}svg{max-width: 90vw;max-height: 90vh;}</style></head><body><svg width="300" height="300" version="1.1" viewBox="0 50 150 150" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><defs><linearGradient id="a" x1="400" x2="325" y1="468.69" y2="425.39" gradientTransform="matrix(3 2.359e-8 -2.359e-8 3 -800 -894.08)" gradientUnits="userSpaceOnUse"><stop stop-color="#8332ff" offset="0"/><stop stop-color="#e816ff" stop-opacity=".99492" offset="1"/></linearGradient><filter id="c" x="-.090127" y="-.084742" width="1.1803" height="1.1695" color-interpolation-filters="sRGB"><feGaussianBlur stdDeviation="3.6361132"/></filter><filter id="b" x="-.060783" y="-.070186" width="1.1216" height="1.1404" color-interpolation-filters="sRGB"><feGaussianBlur stdDeviation="11.396758"/></filter></defs><g transform="translate(179.54 1.2884)"><g transform="matrix(1.1362 0 0 1.1362 14.238 -16.848)"><path d="m-160.68 91.293-1e-5 64.834 56.148 32.417 56.148-32.417 1e-5 -64.834-56.148-32.417z" fill="#191919" stroke-linecap="round" stroke-width="1.1762"/><g transform="matrix(.22459 -.12967 .12967 .22459 -210.23 70.313)" fill="#31e2ff" filter="url(#b)" opacity=".12376" style="mix-blend-mode:normal"><path d="m137.5 187.23-112.5 194.85 112.5 194.86 150-4e-5 -37.5-64.952-75 5e-5 -75-129.9 37.5-64.951h75l-37.5 64.951 75 129.9 150 1e-5 75-129.9-75-129.9h-75l37.5-64.951-75-1e-5 -37.5 64.951-75 1e-5 37.5-64.951zm150 129.9 75-1e-5 37.5 64.951-37.5 64.951-75-2e-5 -37.5-64.951z" stroke-linecap="round" stroke-width="13.606"/><path d="m137.5 187.23-112.5 194.86 125-173.21 62.5-21.65zm150 0-37.5 64.951 50-43.301 62.5-21.648z" opacity=".18033"/><path d="m225 295.48-87.5 21.65h75zm250 86.604-87.5 108.25-137.5 21.65h150zm-200 173.21-137.5 21.65h150z" opacity=".18033"/></g><path d="m-154.03 95.135-2.6e-4 58.351 50.533 29.176 33.689-19.45-16.844-9.7252-16.844 9.7252-33.689-19.45 1e-4 -19.45 16.844-9.7252-1.1e-4 19.45 33.689 19.45 33.689-19.45 1.86e-4 -38.9-33.689-19.45-16.844 9.7252 9e-5 -19.45-16.844 9.7252-8e-5 19.45-16.845 9.7252 1.1e-4 -19.45zm50.533 9.7248 16.844-9.7252 16.844 9.725-8.8e-5 19.45-16.844 9.7252-16.844-9.725z" fill="#840577" fill-opacity=".39927" stroke-linecap="round" stroke-width="3.5286"/><g transform="matrix(.22459 -.12967 .12967 .22459 -210.23 70.313)"><path d="m137.5 187.23-112.5 194.85 112.5 194.86 150-4e-5 -37.5-64.952-75 5e-5 -75-129.9 37.5-64.951h75l-37.5 64.951 75 129.9 150 1e-5 75-129.9-75-129.9h-75l37.5-64.951-75-1e-5 -37.5 64.951-75 1e-5 37.5-64.951zm150 129.9 75-1e-5 37.5 64.951-37.5 64.951-75-2e-5 -37.5-64.951z" fill="url(#a)" stroke-linecap="round" stroke-width="13.606"/><path d="m137.5 187.23-112.5 194.86 125-173.21 62.5-21.65zm150 0-37.5 64.951 50-43.301 62.5-21.648z" fill="#fff" opacity=".18033"/><path d="m225 295.48-87.5 21.65h75zm250 86.604-87.5 108.25-137.5 21.65h150zm-200 173.21-137.5 21.65h150z" opacity=".18033"/></g><path transform="matrix(.98017 0 0 .98017 -2.0724 2.4526)" d="m-71.412 72.221-5.0958 2.9419v5.8849l-15.288 8.8263h-10.192l-10.193 5.8849-13.386 7.7282-1.2015 0.6935-0.70125 0.40462-25.481 14.711v17.654l5.0963 2.9425 10.192-5.8844 5.0963 2.9419v35.307l5.0958 2.9425 20.385-11.769-5.0963-2.9425v-29.422l10.193-5.8844v-5.8849l5.0958-2.9419v-5.8844l5.0963-2.9424v-5.8844l35.673-20.596-5.0963-2.9425v-5.8844l-5.0958 2.9419z" fill="#010101" filter="url(#c)" opacity=".57673" stroke-width=".23538" style="mix-blend-mode:normal"/><g transform="matrix(.1998 -.11536 .11536 .1998 -191.07 74.765)"><path d="m75 338.79 12.5 21.651-75 129.9 12.5 21.651h100l-12.5-21.651 87.5-151.55z" fill="#949fab"/><path d="m12.5 317.14 37.5-64.952h250l37.5 21.651-37.5 64.952h-275z" fill="#e6e9ed" stroke-linecap="round" stroke-width="4.5354"/><path d="m187.5 360.44h50l-12.5 21.651h-50z" fill="#ed5564" stroke-linecap="round" stroke-width="4.5354"/><path d="m300 252.18-25 43.301-12.5-21.651 12.5-21.651h-25l-25 43.301-11.862-20.546 11.862-22.756h-25l-25 43.301-12.351-21.393 12.352-21.908h100z" fill="#8994a2" stroke-linecap="round" stroke-width="4.5354"/><path d="m337.5 273.83-25 43.301 175-1e-5 -12.5-21.651 12.5-21.651z" fill="#949fab" stroke-linecap="round" stroke-width="4.5354"/><path d="m412.5 273.83 12.5-21.651h25l12.5 21.651h-50" fill="#8994a2" stroke-linecap="round" stroke-width="4.5354"/><path d="m200 338.79 75 1e-5 -12.5 21.651h-75z" fill="#8994a2" stroke-linecap="round" stroke-width="4.5354"/><path d="m12.5 317.14 37.5-64.952h250l-237.5 21.651z" fill="#fff" fill-opacity=".096774" stroke-linecap="round" stroke-width="4.5354"/><path d="m112.5 490.34-12.5-21.651 75-129.9h100l-87.5 21.651z" fill-opacity=".096774" stroke-linecap="round" stroke-width="4.5354"/><path d="m325 295.48 12.5-21.651h150z" fill="#fff" fill-opacity=".096774" stroke-linecap="round" stroke-width="4.5354"/><path d="m312.5 317.14 162.5-21.651 12.5 21.651z" fill-opacity=".096774" stroke-linecap="round" stroke-width="4.5354"/><path d="m337.5 273.83-62.5 64.952h25z" fill-opacity=".096774" stroke-linecap="round" stroke-width="4.5354"/><path d="m87.5 360.44-62.5 151.55-12.5-21.651z" fill="#fff" fill-opacity=".096774" stroke-linecap="round" stroke-width="4.5354"/><path d="m175 382.09h-25l12.5 21.651h-25l12.5 21.651h-25l12.5 21.651h-25l12.5 21.651z" fill-opacity=".13109" stroke-linecap="round" stroke-width="4.5354"/><path d="m175 382.09 12.5-21.651h50z" fill-opacity=".13109" stroke-linecap="round" stroke-width="4.5354"/><path d="m412.5 273.83 12.5-21.651h25z" fill="#fff" fill-opacity=".13109" stroke-linecap="round" stroke-width="4.5354"/></g></g></g></svg></body></html>');
65
+
66
+ });
67
+
68
+ // map use
69
+ self.router.use(function(req, res, next){
70
+ req.steps = (req.path) ? req.path.split("/").slice(1) : [];
71
+ if (self.config.maps.hasOwnProperty(req.steps[0])) {
72
+ req.route = "@map";
73
+ req.map = req.steps[0];
74
+ req.tilepath = "/"+req.steps.slice(1).join("/");
75
+ };
76
+ next();
77
+ });
78
+
79
+ // map route
80
+ self.router.route("@map", function(req, res){
81
+
82
+ // mark response stream if something pipes into it
83
+ // to avoid sending to response stream twice
84
+ res.once("pipe", function(){
85
+ res.piped = true;
86
+ });
87
+
88
+ // assemble args and initial data
89
+ const args = {
90
+ req, res,
91
+ data: {
92
+ map: req.map,
93
+ req: {},
94
+ tile: {
95
+ default: true,
96
+ // default tile
97
+ dest: req.path, // destination file - modification use extensions?
98
+ buffer: Buffer.alloc(0), // empty buffer
99
+ status: 204,
100
+ headers: {
101
+ "expires": new Date(Date.now()).toUTCString(),
102
+ }, // response header specific to tile
103
+ // explicit mimetye
104
+ mimetype: "application/octet-stream", // mimetype
105
+ type: "bin", // type
106
+ compression: false,
107
+ language: null,
108
+ expires: true, // instant expires
109
+ },
110
+ tiles: [], // modified versions
111
+ }
112
+ };
113
+
114
+ self.queue.push(function(next){ // queue tasks
115
+
116
+ // create tasks from map
117
+ tasks(self.maps[req.map]).run(args, function(err, { res }){
118
+
119
+ next(); // free queue
120
+
121
+ // FIXME provide more context
122
+ if (err) debug.error(err);
123
+
124
+ // end if response is already sent
125
+ if (!res.writable || res.destroyed || res.finished || res.closed || res.piped) return; // FIXME handle errors anyway
126
+
127
+ // send default error FIXME configure verbose errors
128
+ if (err) {
129
+ res.statusCode = 500;
130
+ res.setHeader("content-type", "text/plain");
131
+ res.end("Error.");
132
+ return;
133
+ };
134
+
135
+ // default response: no content
136
+ res.statusCode = 200; // FIXME 204
137
+ res.setHeader("content-length", "0");
138
+ res.end("");
139
+ return;
140
+
141
+ });
142
+
143
+ });
144
+
145
+ });
146
+
147
+ // listen
148
+ self.servers = [];
149
+ self.listen(self.router.serve);
150
+
151
+ // handle messages from main thred
152
+ process.on("message", function(message){
153
+ if (message === "shutdown") self.shutdown();
154
+ // FIXME: reload config without shutdown?
155
+ });
156
+
157
+ // shutdown on SIGINT
158
+ process.on("SIGINT", function(){
159
+ debug.info("SIGINT received");
160
+ self.shutdown();
161
+ });
162
+
163
+ // shutdown on SIGTERM
164
+ process.on("SIGTERM", function(){
165
+ debug.info("SIGTERM received");
166
+ self.shutdown();
167
+ });
168
+
169
+ return this;
170
+ };
171
+
172
+ // graceful shutdown
173
+ tileblaster.prototype.shutdown = function(){
174
+ const self = this;
175
+ self.shutdown = function(){}; // ensure shutdown only runs once
176
+ debug.info("shutting down");
177
+ let closed = 0;
178
+ self.servers.forEach(function(server){
179
+ server.close(function(){
180
+ (function(fn){
181
+ if (!server.socket) return fn();
182
+ fs.unlink(server.socket, fn);
183
+ })(function(){
184
+ if (++closed === self.servers.length) {
185
+ debug.info("All Servers Closed");
186
+ process.exit(0);
187
+ }
188
+ });
189
+ });
190
+ });
191
+ // watchdog
192
+ setTimeout(function(){
193
+ debug.warn("Closing servers timed out");
194
+ process.exit(1);
195
+ },3000);
196
+ };
197
+
198
+ // prepare jobs for maps
199
+ tileblaster.prototype.prepareMaps = function(){
200
+ const self = this;
201
+ self.maps = Object.entries(self.config.maps).reduce(function(maps, [ mapid, map ]){
202
+ maps[mapid] = self.prepareJobs(mapid, map);
203
+ return maps;
204
+ },{});
205
+
206
+ return this;
207
+ };
208
+
209
+ // prepare jobs FIXME merge with prepareMaps?
210
+ tileblaster.prototype.prepareJobs = function(mapid, map){
211
+ const self = this;
212
+ return map.map(function(job){
213
+
214
+ // job is a straight function
215
+ if (typeof job === "function") return job;
216
+
217
+ // if not an object, passthrough
218
+ if (typeof job !== "object") {
219
+ debug.warn("Invalid Task in map '%s'", mapid);
220
+ return function({}, next){ next(); };
221
+ };
222
+
223
+ // job is builtin
224
+ if (job.hasOwnProperty("builtin")) {
225
+ if (self.builtins.hasOwnProperty(job.builtin)) {
226
+ const fn = function(args, next, skip){
227
+ return self.builtins[job.builtin].call(self, { ...args, opts: job }, next, skip);
228
+ };
229
+ fn.label = job.builtin;
230
+ return fn;
231
+ } else {
232
+ // unknown plugin, pass through
233
+ debug.warn("Unknown builtin '%s' in map '%s'", job.builtin, mapid);
234
+ return function({}, next){ next(); };
235
+ };
236
+ };
237
+
238
+ // job is plugin
239
+ if (job.hasOwnProperty("plugin")) {
240
+ if (self.plugins.hasOwnProperty(job.plugin)) {
241
+ const fn = function(args, next, skip){
242
+ return self.plugins[job.plugin].call(self, { ...args, opts: job }, next, skip);
243
+ };
244
+ fn.label = "plugin:"+job.plugin;
245
+ return fn;
246
+ } else {
247
+ // unknown plugin, pass through
248
+ debug.warn("Unknown plugin '%s' in map '%s'", job.plugin, mapid);
249
+ return function({}, next){ next(); };
250
+ };
251
+ };
252
+
253
+ // unknown job type, passthrough with warning
254
+ debug.warn("Invalid Task type in map '%s'", reqmapid);
255
+ return function({}, next){ next(); };
256
+
257
+ });
258
+ };
259
+
260
+ // load builtins
261
+ tileblaster.prototype.loadBuiltins = function(builtins){
262
+ const self = this;
263
+ self.builtins = builtins.reduce(function(builtins, builtin){
264
+ let builtinPath = path.resolve(__dirname,"builtins",builtin);
265
+ if (load.exists(builtinPath)) {
266
+ debug.info("Loaded builtin '%s'", builtin);
267
+ builtins[builtin] = require(builtinPath);
268
+ } else {
269
+ debug.warn("Missing builtin: '%s'", builtin);
270
+ };
271
+ return builtins;
272
+ },{});
273
+ return this;
274
+ };
275
+
276
+ // load plugins
277
+ tileblaster.prototype.loadPlugins = function(){
278
+ const self = this;
279
+
280
+ self.plugins = Object.entries(self.config.plugins).reduce(function(plugins, [ name, plugin ]){
281
+
282
+ let pluginname = name.trim().toLowerCase().replace(/[^a-z0-9\-\_\.]+/g,'');
283
+ if (pluginname !== name) debug.warn("Warning: Plugin name has been sanitized: '%s' → '%s'", name, pluginname);
284
+
285
+ try {
286
+ if (load.exists(plugin)) {
287
+ plugins[pluginname] = require(plugin);
288
+ debug.info("Loaded Plugin '%s'", pluginname);
289
+ } else {
290
+ let localPlugin = path.resolve(self.config.paths.plugins, plugin);
291
+ if (load.exists(localPlugin)) {
292
+ plugins[pluginname] = require(localPlugin);
293
+ debug.info("Loaded Plugin '%s'", pluginname);
294
+ } else {
295
+ let packagePlugin = path.resolve(__dirname, "plugins", plugin);
296
+ if (load.exists(packagePlugin)) {
297
+ plugins[pluginname] = require(packagePlugin);
298
+ debug.info("Loaded Plugin '%s'", pluginname);
299
+ } else {
300
+ debug.warn("Error loading plugin '%s': Not found", pluginname);
301
+ }
302
+ }
303
+ }
304
+ } catch(err) {
305
+ debug.warn("Error loading plugin '%s':", pluginname, err);
306
+ }
307
+
308
+ return plugins;
309
+
310
+ },{});
311
+
312
+ return this;
313
+ };
314
+
315
+ // read config and fill in the blanks
316
+ tileblaster.prototype.configure = function(config){
317
+ const self = this;
318
+
319
+ // check config file version
320
+ if (!config.hasOwnProperty("version") || config.version !== 1) {
321
+ debug.error("Config has no version property. Possibly an old config file? Exiting.")
322
+ process.exit(1);
323
+ };
324
+
325
+ // check if any maps are configured
326
+ if (!config.hasOwnProperty("maps") || Object.keys(config.maps) === 0) {
327
+ debug.error("Config has no maps configured. Exiting.")
328
+ process.exit(1);
329
+ };
330
+
331
+ // check if any listen instructions are given
332
+ if (!config.hasOwnProperty("listen") || config.listen.length === 0) {
333
+ debug.error("Config has no listen instructions. Exiting.")
334
+ process.exit(1);
335
+ };
336
+
337
+ // set config
338
+ self.config = {
339
+ id: "tileblaster",
340
+ threads: 1,
341
+ queue: 12,
342
+ url: null,
343
+ mount: null,
344
+ paths: {},
345
+ plugins: {},
346
+ listen: [],
347
+ maps: {},
348
+ ...config,
349
+ };
350
+
351
+ // clamp number of threads to number of cores and queue size
352
+ self.config.threads = Math.max(1, Math.min(self.config.threads, os.cpus().length));
353
+ self.config.queue = Math.max(1, Math.min(self.config.queue, 100));
354
+
355
+ // warn user if queue size is less than 12 over all threads
356
+ if ((self.config.queue * self.config.threads) < 12) debug.warn("Warning: Queue size of %d is pretty small.", self.config.queue);
357
+
358
+ // url and mount
359
+ if (!self.config.server.url) self.config.server.url = "/";
360
+ if (!self.config.server.mount) self.config.server.mount = (self.config.server.url === "/") ? "/" : self.config.server.url.replace(/^https?:\/\/.*?\//,"/");
361
+
362
+ // remove trailing slashes from url and mount
363
+ while (self.config.server.url.length > 1 && self.config.server.url.charCodeAt(self.config.server.url.length-1) === 47) self.config.server.url = self.config.server.url.slice(0, -1);
364
+ while (self.config.server.mount.length > 1 && self.config.server.mount.charCodeAt(self.config.server.mount.length-1) === 47) self.config.server.mount = self.config.server.mount.slice(0, -1);
365
+
366
+ // paths
367
+ if (!self.config.paths.work) self.config.paths.work = path.resolve(os.homedir(), "tileblaster");
368
+ ["data","logs","plugins","sockets"].forEach(function(p){
369
+ if (!self.config.paths[p]) self.config.paths[p] = path.resolve(self.config.paths.work, p);
370
+ });
371
+
372
+ // default host to localhost if port is set
373
+ if (self.config.port && !self.config.host) self.config.host = "localhost";
374
+
375
+ // listen
376
+ self.config.listen = self.config.listen.filter(function(listen){
377
+ return listen.hasOwnProperty("port") || listen.hasOwnProperty("socket");
378
+ }).map(function(listen){
379
+ // ensure hostname is set
380
+ if (listen.hasOwnProperty("port")) {
381
+ if (typeof listen.port !== "number") listen.port = parseInt(listen.port,10);
382
+ if (!listen.host) listen.host = "localhost";
383
+ }
384
+ else if (listen.hasOwnProperty("socket")) {
385
+ listen.socket = path.resolve(self.config.paths.sockets, listen.socket);
386
+ };
387
+ return listen;
388
+ }).filter(function(listen){
389
+ // check for port and host types and NaN
390
+ return listen.socket || (!isNaN(listen.port) && typeof listen.host === "string");
391
+ });
392
+
393
+ // check again if any listen instructions are given
394
+ if (self.config.listen.length === 0) {
395
+ debug.error("Config has no valid listen instructions. Exiting.")
396
+ process.exit(1);
397
+ };
398
+
399
+ // maps
400
+ self.config.maps = Object.entries(config.maps).reduce(function(maps, [ id, map ]){
401
+ let mapid = id.trim().toLowerCase().replace(/[^a-z0-9\-\_\.]+/g,'');
402
+ if (mapid !== id) debug.warn("Warning: Map id has been sanitized: '%s' → '%s'", id, mapid);
403
+ maps[mapid] = map;
404
+ return maps;
405
+ },{});
406
+
407
+ return this;
408
+ };
409
+
410
+ // listen handler
411
+ tileblaster.prototype.listen = function(router){
412
+ const self = this;
413
+
414
+ self.config.listen.forEach(function(listen){
415
+
416
+ const server = http.createServer({ keepAlive: true }, function(){
417
+ router.call(self.router, ...arguments);
418
+ });
419
+
420
+ if (listen.port) {
421
+
422
+ server.listen(listen.port, listen.port, function(err){
423
+ if (err) return debug.error("listen: ERROR binding port '%s:%d':", listen.host, listen.port, err);
424
+ debug.info("Listening on '%s:%d'", listen.host, listen.port);
425
+ self.servers.push(server);
426
+ });
427
+
428
+ } else if (listen.socket) {
429
+
430
+ // create different sockets per instance
431
+ if (self.config.threads > 1) {
432
+ let ext = path.extname(listen.socket);
433
+ listen.socket = path.join(path.dirname(listen.socket), path.basename(listen.socket, ext) + self.config.id + ext);
434
+ }
435
+
436
+ // ensure socket dir exists
437
+ fs.mkdir(path.dirname(listen.socket), { recursive: true }, function(err){
438
+ if (err && err.code !== "ENOENT") return debug.error("Creating socket dir '%s':", path.dirname(listen.socket), err);
439
+
440
+ // inlink old socket
441
+ fs.unlink(listen.socket, function(err) { // try unlink leftover socket
442
+ if (err && err.code !== "ENOENT") return debug.error("Deleting socket '%s':", listen.socket, err);
443
+
444
+ server.listen(listen.socket, function(err) {
445
+ if (err) return debug.error("Binding to socket '%s':", listen.socket, err);
446
+
447
+ // store socket path
448
+ server.socket = listen.socket;
449
+
450
+ debug.info("Listening on socket '%s'", listen.socket);
451
+ self.servers.push(server);
452
+ if (listen.mode) fs.chmod(listen.socket, listen.mode, function(err){
453
+ if (err) return debug.error("Changing permissions of socket '%s' to '%s':", listen.socket, listen.perms.toString(8), err);
454
+ });
455
+ if (listen.group) fs.chown(listen.socket, os.userInfo().uid, listen.group, function(err){
456
+ if (err) return debug.error("Changing gid of socket '%s' to '%s':", listen.socket, listen.gid, err);
457
+ });
458
+
459
+ });
460
+ });
461
+
462
+ });
463
+
464
+ }
465
+
466
+ });
467
+
468
+ return self;
469
+ };
package/LICENSE DELETED
@@ -1,21 +0,0 @@
1
- This is free and unencumbered software released into the public domain.
2
-
3
- Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
4
- software, either in source code form or as a compiled binary, for any purpose,
5
- commercial or non-commercial, and by any means.
6
-
7
- In jurisdictions that recognize copyright laws, the author or authors of this
8
- software dedicate any and all copyright interest in the software to the public
9
- domain. We make this dedication for the benefit of the public at large and to
10
- the detriment of our heirs and successors. We intend this dedication to be an
11
- overt act of relinquishment in perpetuity of all present and future rights to
12
- this software under copyright law.
13
-
14
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
- AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
18
- ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
19
- WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20
-
21
- For more information, please refer to <https://unlicense.org/>
package/config.js.dist DELETED
@@ -1,81 +0,0 @@
1
- module.exports = {
2
-
3
- // path to socket file
4
- "socket": "/path/to/tileblaster.sock",
5
-
6
- // path to tile directory
7
- "tiles": "/path/to/tiles",
8
-
9
- // parallel request queue size
10
- "queue": 100,
11
-
12
- // parallel request queue size
13
- "id": "tileblaster",
14
-
15
- // provide errors as response headers
16
- "hints": false,
17
-
18
- // nsa heartbeat server, if so desired
19
- "heartbeat": "udp4://nsa.example.com:30826",
20
-
21
- // clean up every so often
22
- "cleanup": "15m",
23
-
24
- // maps
25
- "maps": {
26
-
27
- // map id, accessible via //server/mapid/z/x/y.ext
28
- "example": {
29
-
30
- // backend url:
31
- // * {s} subdomains specified in sub
32
- // * {z}, {x}, {y} tile coordinates
33
- // * {e} extension
34
- // * {r} resolution marker
35
- "url": "https://{s}.tiles.example.com/tiles/{z}/{x}/{y}{r}.{e}",
36
-
37
- // url points to versatiles container (→ https://github.com/versatiles-org/versatiles-spec)
38
- "versatiles": false,
39
-
40
- // backend uses tms instead of zxy
41
- "tms": false,
42
-
43
- // possible extensions
44
- "ext": ["mvt","json","topojson","png","jpg"],
45
-
46
- // valid zoom levels
47
- "zoom": [0,16],
48
-
49
- // valid subdomains
50
- "sub": ["a","b","c","d"],
51
-
52
- // bounding box, west, south, east, north
53
- "bbox": [-180,-90,180,90],
54
-
55
- // valid mimetypes from backend server
56
- "mime": ["application/vnd.mapbox-vector-tile","application/x-protobuf","application/json"],
57
-
58
- // valid resolution markers
59
- "res": ["@2x","@4x"],
60
-
61
- // optimize tiles
62
- // * .png with `optipng`
63
- // * .jpg with `mozjpeg`
64
- "optimize": true,
65
-
66
- // compress tiles (only makes sense for non-rastered tiles like json, pbf, mvt)
67
- "compress": [ "gz", "br" ],
68
-
69
- // write tiles to disk
70
- "cache": true,
71
-
72
- // minimum time tiles are kept
73
- "expires": "1d",
74
-
75
- },
76
-
77
- // more backends here
78
-
79
- },
80
-
81
- };