Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 69 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# Version control
.git
.gitignore

# Node.js
node_modules
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.npm

# Development files
.dockerignore
Dockerfile*
docker-compose*.yml
.env.local
.env.development.local
.env.test.local
.env.production.local

# IDE and editor files
.vscode
.idea
*.swp
*.swo
*~
.nova

# OS generated files
.DS_Store
.DS_Store?
._*
.Spotlight-V100
.Trashes
ehthumbs.db
Thumbs.db

# Logs
logs
*.log

# Runtime data
pids
*.pid
*.seed
*.pid.lock

# Coverage directory used by tools like istanbul
coverage

# Documentation
docs/
readme.md
*.md

# Config and temporary files
config.js
*.socket
attic

# Test files
test/
tests/
*.test.js
*.spec.js

# Build artifacts
dist/
build/
13 changes: 13 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
FROM node:22-alpine
WORKDIR /usr/src/app
RUN apk add --no-cache python3 make g++

# Copy package.json and package-lock.json first to leverage caching
COPY package*.json ./
RUN npm install

# Copy the rest of the application files
COPY . .

ENTRYPOINT ["node", "./bin/tileblaster.js"]
CMD []
13 changes: 13 additions & 0 deletions bin/tileblaster.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,19 @@ if (require.main === module) {
function fork(n){
worker = cluster.fork({ ...process.env, workerid: n });
debug.info("Started Worker #%d".bold.white, n);

// Handle messages from workers
worker.on('message', function(message) {
if (typeof message === 'object' && message.type === 'fatal-error') {
debug.error("Worker #%d reported fatal error: %s".bold.red, n, message.message);
if (message.error === 'EADDRINUSE') {
debug.error("Address already in use - initiating complete shutdown".bold.red);
shutdown();
return;
}
}
});

worker.on('disconnect', function(){
debug.warn("Disconnect from Worker #%d".bold.white, n);
if (shuttingdown) { // remove worker from pool
Expand Down
37 changes: 24 additions & 13 deletions lib/purge.js
Original file line number Diff line number Diff line change
Expand Up @@ -84,20 +84,31 @@ if (worker.isMainThread) {
Promise.allSettled(caches.map(function(cache){
return new Promise(function(resolve,reject){

let deletable = [];

klaw(cache.dir).on("data", function(file){
if (file.stats.mtimeMs > cache.expires) deletable.push(file.path);
}).on("end", function(){
Promise.allSettled(deletable.map(function(file){
return new Promise(function(resolve,reject){
fs.unlink(file, function(err){
if (err) return reject(err);
purged++;
resolve();
// check if directory exists before trying to walk it
fs.access(cache.dir, fs.constants.F_OK, function(err){
if (err) {
// directory doesn't exist, nothing to purge
return resolve();
}

let deletable = [];

klaw(cache.dir).on("data", function(file){
if (file.stats.mtimeMs > cache.expires) deletable.push(file.path);
}).on("end", function(){
Promise.allSettled(deletable.map(function(file){
return new Promise(function(resolve,reject){
fs.unlink(file, function(err){
if (err) return reject(err);
purged++;
resolve();
});
});
});
})).then(resolve).catch(reject);
})).then(resolve).catch(reject);
}).on("error", function(err){
// handle klaw errors gracefully
reject(err);
});
});
});
})).then(function(){
Expand Down
4 changes: 4 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ if you're allowed to do so.
* `-v` `--verbose` - enable debug output
* `-q` `--quiet` - disable debug output

## Docker usage
`docker build . -t tileblaster`
`docker run -v <config-path>:/usr/src/app/config.js tileblaster -c ./config.js -v`

## Configuration

See [Configuration](docs/config.md) and [Examples](docs/examples.md)
Expand Down
31 changes: 29 additions & 2 deletions tileblaster.js
Original file line number Diff line number Diff line change
Expand Up @@ -434,10 +434,30 @@ tileblaster.prototype.listen = function(router){
router.call(self.router, ...arguments);
});

// Handle server errors (like EADDRINUSE) before they become unhandled
server.on('error', function(err) {
debug.error("Server error:", err);
if (err.code === 'EADDRINUSE') {
debug.error("Address already in use, starting shutdown process");
// Notify main process about fatal error before shutting down
if (process.send) {
process.send({ type: 'fatal-error', error: 'EADDRINUSE', message: 'Address already in use' });
}
self.shutdown();
}
});

if (listen.port) {

server.listen(listen.port, listen.host, function(err){
if (err) return debug.error("listen: ERROR binding port '%s:%d':", listen.host, listen.port, err);
if (err) {
debug.error("listen: ERROR binding port '%s:%d':", listen.host, listen.port, err);
if (err.code === 'EADDRINUSE') {
debug.error("Port %d is already in use, starting shutdown process", listen.port);
self.shutdown();
}
return;
}
debug.info("Listening on '%s:%d'", listen.host, listen.port);
self.servers.push(server);
});
Expand All @@ -459,7 +479,14 @@ tileblaster.prototype.listen = function(router){
if (err && err.code !== "ENOENT") return debug.error("Deleting socket '%s':", listen.socket, err);

server.listen(listen.socket, function(err) {
if (err) return debug.error("Binding to socket '%s':", listen.socket, err);
if (err) {
debug.error("Binding to socket '%s':", listen.socket, err);
if (err.code === 'EADDRINUSE') {
debug.error("Socket %s is already in use, starting shutdown process", listen.socket);
self.shutdown();
}
return;
}

// store socket path
server.socket = listen.socket;
Expand Down