Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
107 commits
Select commit Hold shift + click to select a range
876efc4
added workflow for develop branch
Aug 18, 2025
06473ba
Fixed issue with key algorithm
Aug 18, 2025
192a7f8
Fix lazy loading of load balancer certificates (#32)
alxnik Aug 19, 2025
30cf950
fixing ui stuff
Aug 19, 2025
a9f78ed
very simple readme
Aug 19, 2025
50cf672
Better exception handling
Aug 19, 2025
8f60383
feat: select condition type when adding rule condition (#33)
alxnik Aug 19, 2025
29a89de
Refactored rule dialog
Aug 19, 2025
59e5b9b
switched order
Aug 19, 2025
54c3a59
Enable statistics
Aug 19, 2025
a626b1c
Initial implementation of haproxy statistics + infra
Aug 28, 2025
b029d23
Added timestamp to statistics
Aug 30, 2025
1e48f11
Kinda working monitoring charts
Aug 30, 2025
3426fce
Bug investigation
Aug 30, 2025
2c49ddd
cleanup
Aug 30, 2025
410dde2
Cleanup of ui
Aug 31, 2025
1ff3444
Created new statistics service
Aug 31, 2025
47f1b22
cleanup
Aug 31, 2025
6ba4fb9
Initial setup for monitoring with collectd
Sep 7, 2025
fd927ed
Kinda working collectd
Oct 26, 2025
6b8b380
Doing stuff with influxdb
Nov 3, 2025
5762cc1
added x-forwarded-for
Nov 3, 2025
4c467dd
Added default headers
Nov 3, 2025
161ecbe
add status in detail page
Nov 9, 2025
9657693
fixibug
Nov 9, 2025
38fa1e5
Proper item refreshing
Nov 9, 2025
af99c7c
Fixes #34
Nov 9, 2025
896d22f
Fixes #36
Nov 9, 2025
28e5ebd
Fixes #37
Nov 9, 2025
8cc4ac9
fixing issue with negative priorities
Nov 9, 2025
fb045fe
Fixes #31
Nov 9, 2025
f1a75d5
Fixes #31 but better
Nov 9, 2025
94144e2
Added handling of tls load balancer without certificates
Nov 9, 2025
ae1554f
Fixed bug with maxpriority when there are no rules
Nov 9, 2025
b2e9241
Merge branch 'develop' into monitoring
Nov 10, 2025
c335466
Fixing telegraf
Nov 16, 2025
5df459d
Added functionality for http apis
Nov 16, 2025
6b4da9c
First working prototype of influxdb working?
Nov 16, 2025
4b1aff2
fixing stuff
Nov 16, 2025
ae32a10
Fix proper expiration marking
Nov 17, 2025
fab3576
Implemented automated certificate renewal
Nov 17, 2025
26c40a8
Added ui method to renew cert
Nov 17, 2025
36b82d0
Merge branch 'develop' into monitoring
Nov 18, 2025
d1f2b78
Removed legacy background monitoring job
Nov 18, 2025
ed8ce68
Cleanup of previous monitoring implementation
Nov 18, 2025
308a72a
cleaner name for monitoring name
Nov 18, 2025
621df22
Refactoring statistics
Nov 23, 2025
f676ed9
First kinda working monitoring implementation
Nov 26, 2025
b2658dd
Clean-ish ui for chart
Nov 26, 2025
d6c825c
Had to finally implement a simple token vault
Nov 27, 2025
4147390
Implemented influxdb2 support
Nov 29, 2025
15fb9e4
Added better timeout
Nov 30, 2025
ca410dd
Added logging to the HTTP api
Nov 30, 2025
ce44fd3
dumb test
Nov 30, 2025
e9501e4
In case tilework is containerized, add it to the default network
Nov 30, 2025
8ef76b7
Correct way to initialize docker
Nov 30, 2025
7fc3994
Better wait for influx to start. Also don't needlessly restart it
Nov 30, 2025
2d22867
Reorganised the way monitoring data is saved and queried
Dec 6, 2025
e99aabe
Added requests too
Dec 6, 2025
8e3596d
We have 2 mostly working graphs
Dec 6, 2025
e70310e
Minor prettification
Dec 6, 2025
136e3fc
added refresh button
Dec 6, 2025
3275791
cleanup
Dec 7, 2025
58e3959
cleanup
Dec 7, 2025
dcb4b6f
update
Dec 7, 2025
a50e3ce
Initial implementation of time range selection
Dec 7, 2025
9b93f0a
Default charts will be in local browser timezone
Dec 7, 2025
dba10a4
Added fill of missing data + interval implementation
Dec 7, 2025
cb8744f
Simplified certificate signing. Fixes #45
Dec 7, 2025
63136ba
Fixes #38
Dec 7, 2025
d84c40e
Cleaned up container code
Dec 25, 2025
2c8fe1a
Toned down the pills
Dec 25, 2025
b0e277d
UI tweaks
Dec 25, 2025
9591928
Ui tweaks
Dec 25, 2025
a7a05f0
UI tweaks
Dec 25, 2025
970bfad
Fix concurrency issue
Dec 26, 2025
eb938f4
removed debug log
Dec 26, 2025
c4ead49
Refactored haproxy configurator
Dec 26, 2025
330dfb4
Fixes #30
Dec 26, 2025
2148134
deduplicate ports
Dec 26, 2025
265417d
Fixes #13
Dec 26, 2025
1c8aee1
Better certificate details
Dec 26, 2025
baab90a
Allow renewal of revoked certificates
Dec 26, 2025
f00f3b8
Refactored certificate authority to have bring more data to the UI
Dec 27, 2025
9be568f
Fixed bugs in container deletion
Dec 27, 2025
1f0756f
cleanup
Dec 27, 2025
c2f1d6c
minor fix
Dec 27, 2025
344182c
Scoped configuration application to lb
Dec 27, 2025
82626aa
Refactored chart panel
Dec 27, 2025
c15628d
Further seperation of charts to a distinct component
Dec 27, 2025
e3338e8
fixin bugs
Dec 27, 2025
8c28048
Added interval selection
Dec 28, 2025
aafb0e5
ui tweak
Dec 28, 2025
6400043
Added tab in the url
Dec 28, 2025
007c8c7
monitoring bugfixes
Dec 28, 2025
6c1562a
further cleanup of haproxy monitoring tags
Dec 29, 2025
e974cee
Initial support for more metrics
Dec 31, 2025
624b0b2
more responsive charts
Dec 31, 2025
7c8502c
tweaking
Dec 31, 2025
af64434
more tweaking
Dec 31, 2025
ff90f4b
tweaking
Dec 31, 2025
ac1eb4b
Tweaks
Jan 1, 2026
47af6d0
Update README.md
alxnik Jan 1, 2026
b0c6d27
Update README.md
alxnik Jan 1, 2026
0226024
Update README.md
alxnik Jan 1, 2026
dd770b9
Switched to chart.js
Jan 1, 2026
82d1b89
Merge branch 'develop' of https://github.com/tileworkdev/tilework int…
Jan 1, 2026
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
2 changes: 2 additions & 0 deletions .github/workflows/main.yml → .github/workflows/docker.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ on:
push:
branches:
- main
- develop

jobs:
build-and-push:
Expand Down Expand Up @@ -35,6 +36,7 @@ jobs:
type=ref,event=branch
type=semver,pattern={{version}}
type=raw,value=latest,enable=${{ github.ref == 'refs/heads/main' }}
type=raw,value=develop,enable=${{ github.ref == 'refs/heads/develop' }}

- name: Build and push Docker image
uses: docker/build-push-action@v6
Expand Down
49 changes: 49 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# Tilework

## About

Tilework is a fully integrated reverse proxying and load balancing platform. It is designed with the main objective of being simple and fast to configure rather than being in the way.

## Features
- Deployment of HTTP/TCP/UDP load balancers with multiple backends
- HTTP rules based routing, including hostname, URL path, query string
- Certificate issuing via popular services, lifecycle management, auto-renewal
- Realtime and historical service statistics
- Docker based service deployment - no disruption of the host environement


## Install
1. Install [docker engine](https://docs.docker.com/engine/install/) or [docker desktop](https://docs.docker.com/get-started/get-docker/). Be sure that docker compose is also installed.




2. Create a docker-compose.yml file as follows:

```yaml
services:
tileworkui:
image: tilework/tilework:latest
ports:
- 5180:5180
environment:
- ASPNETCORE_ENVIRONMENT=Docker
volumes:
- tilework_data:/var/lib/tilework
- /var/run/docker.sock:/var/run/docker.sock

volumes:
tilework_data:
external: false
```

3. Start the service up
```
# If using the docker-compose command
docker-compose up -d

# If using docker-compose-plugin
docker compose up -d
```

4. Navigate your browser to http://\<host>:5180
4 changes: 4 additions & 0 deletions tilework.core/Attributes/CumulativeAttribute.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
namespace Tilework.Core.Attributes;

[AttributeUsage(AttributeTargets.Property)]
public sealed class CumulativeAttribute : Attribute { }
5 changes: 5 additions & 0 deletions tilework.core/Enums/Core/ContainerExceptionType.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
namespace Tilework.Core.Enums;
public enum ContainerExceptionType
{
PORT_CONFLICT
}
7 changes: 7 additions & 0 deletions tilework.core/Enums/Core/ContainerRestartType.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace Tilework.Core.Enums;
public enum ContainerRestartType
{
SIGNAL,
RESTART,
RECREATE
}
File renamed without changes.
6 changes: 6 additions & 0 deletions tilework.core/Enums/Monitoring/MonitoringPersistenceType.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace Tilework.Monitoring.Enums;

public enum MonitoringPersistenceType
{
INFLUXDB
}
6 changes: 6 additions & 0 deletions tilework.core/Enums/Monitoring/MonitoringSourceType.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace Tilework.Monitoring.Enums;

public enum MonitoringSourceType
{
HAPROXY
}
12 changes: 12 additions & 0 deletions tilework.core/Events/Events/CertificateRenewed.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using Coravel.Events.Interfaces;

using Tilework.CertificateManagement.Models;

namespace Tilework.Events;

public class CertificateRenewed : IEvent
{
public CertificateDTO Certificate { get; set; }

public CertificateRenewed(CertificateDTO certificate) => Certificate = certificate;
}
26 changes: 26 additions & 0 deletions tilework.core/Events/Listeners/LoadBalancerCertificateListener.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
using Microsoft.Extensions.Logging;

using Coravel.Events.Interfaces;

using Tilework.LoadBalancing.Interfaces;

namespace Tilework.Events;

public class LoadBalancerCertificateListener : IListener<CertificateRenewed>
{
private readonly ILoadBalancerService _loadBalancer;
private readonly ILogger<LoadBalancerCertificateListener> _logger;

public LoadBalancerCertificateListener(ILoadBalancerService loadBalancer,
ILogger<LoadBalancerCertificateListener> logger)
{
_loadBalancer = loadBalancer;
_logger = logger;
}

public async Task HandleAsync(CertificateRenewed evt)
{
_logger.LogInformation($"Applying load balancer configuration due to renewal of certificate {evt.Certificate.Name}");
await _loadBalancer.ApplyConfiguration();
}
}
30 changes: 28 additions & 2 deletions tilework.core/Exceptions/Core/DockerException.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,37 @@
using System.Text.Json;
using System.Text.RegularExpressions;
using Tilework.Core.Enums;

namespace Tilework.Exceptions.Core;

public class DockerException : Exception
{
public DockerException(string message) : base(ParseResponseBody(message))
public ContainerExceptionType? Type { get; set; } = null;

private static readonly Regex PortConflictRegex = new(
@"failed\s+to\s+bind\s+host\s+port\s+[^\s:]+:\d+(?:/\w+)?:\s+address\s+already\s+in\s+use",
RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.CultureInvariant);

public DockerException(string message) : base(BuildExceptionMessage(message, out var parsedMessage))
{
Type = DetectExceptionType(parsedMessage);
}

private static ContainerExceptionType? DetectExceptionType(string? message)
{
if (string.IsNullOrWhiteSpace(message))
return null;

if (PortConflictRegex.IsMatch(message))
return ContainerExceptionType.PORT_CONFLICT;

return null;
}

private static string BuildExceptionMessage(string message, out string? parsedMessage)
{
parsedMessage = ParseResponseBody(message);
return parsedMessage ?? message;
}

private static string? ParseResponseBody(string message)
Expand All @@ -19,4 +45,4 @@ public DockerException(string message) : base(ParseResponseBody(message))
public class ResponseBody
{
public string message { get; set; }
}
}
12 changes: 12 additions & 0 deletions tilework.core/Exceptions/Core/PortConflictException.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using System.Text.Json;
using System.Text.RegularExpressions;
using Tilework.Core.Enums;

namespace Tilework.Exceptions.Core;

public class PortConfictException : Exception
{
public PortConfictException(string? message = null) : base(message)
{
}
}
18 changes: 15 additions & 3 deletions tilework.core/Initializers/CertificateManagementInitializer.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Hosting;

using Microsoft.Extensions.DependencyInjection;

using Coravel;
using Tilework.Core.Jobs.CertificateManagement;


namespace Tilework.CertificateManagement.Services;
Expand All @@ -18,11 +19,22 @@ public CertificateManagementInitializer(ILogger<CertificateManagementInitializer
_serviceProvider = serviceProvider;
}

public Task StartAsync(CancellationToken ct) => Task.CompletedTask;
public async Task StartAsync(CancellationToken ct)
{
_logger.LogInformation($"Initiating startup for module: CertificateManagement");
await using var scope = _serviceProvider.CreateAsyncScope();

scope.ServiceProvider.UseScheduler(s =>
{
s.Schedule<CertificateRenewalJob>()
.Hourly()
.PreventOverlapping("CertificateRenewalJob");
});
}

public async Task StopAsync(CancellationToken ct)
{
_logger.LogInformation($"Initiating shutdown for module: LoadBalancing");
_logger.LogInformation($"Initiating shutdown for module: CertificateManagement");
await using var scope = _serviceProvider.CreateAsyncScope();
var loadBalancerService = scope.ServiceProvider.GetRequiredService<AcmeVerificationService>();

Expand Down
8 changes: 8 additions & 0 deletions tilework.core/Initializers/CoreInitializer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.EntityFrameworkCore;

using Coravel;

using Tilework.Core.Persistence;
using Tilework.Core.Interfaces;

namespace Tilework.Core.Services;

Expand All @@ -27,6 +30,11 @@ public async Task StartAsync(CancellationToken ct)
_logger.LogInformation($"Running migrations for context: TileworkContext");
var dbContext = scope.ServiceProvider.GetRequiredService<TileworkContext>();
await dbContext.Database.MigrateAsync(ct);

scope.ServiceProvider.ConfigureEvents();

var containerManager = scope.ServiceProvider.GetRequiredService<IContainerManager>();
containerManager.Initialize();
}


Expand Down
9 changes: 8 additions & 1 deletion tilework.core/Initializers/LoadBalancingInitializer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.DependencyInjection;

using Coravel;

using Tilework.LoadBalancing.Interfaces;
using Tilework.Events;

namespace Tilework.LoadBalancing.Services;

Expand All @@ -25,6 +28,10 @@ public async Task StartAsync(CancellationToken ct)

var loadBalancerService = scope.ServiceProvider.GetRequiredService<ILoadBalancerService>();
await loadBalancerService.ApplyConfiguration();

var events = scope.ServiceProvider.ConfigureEvents();
events.Register<CertificateRenewed>()
.Subscribe<LoadBalancerCertificateListener>();
}

public async Task StopAsync(CancellationToken ct)
Expand All @@ -35,4 +42,4 @@ public async Task StopAsync(CancellationToken ct)

await loadBalancerService.Shutdown();
}
}
}
40 changes: 40 additions & 0 deletions tilework.core/Initializers/MonitoringInitializer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.DependencyInjection;

using Coravel;

using Tilework.Monitoring.Services;

namespace Tilework.LoadBalancing.Services;

public sealed class MonitoringInitializer : IHostedService
{
private readonly IServiceProvider _serviceProvider;
private readonly ILogger<MonitoringInitializer> _logger;

public MonitoringInitializer(ILogger<MonitoringInitializer> logger,
IServiceProvider serviceProvider)
{
_logger = logger;
_serviceProvider = serviceProvider;
}

public async Task StartAsync(CancellationToken ct)
{
_logger.LogInformation($"Initiating startup for module: Monitoring");
await using var scope = _serviceProvider.CreateAsyncScope();

var dataCollectorService = scope.ServiceProvider.GetRequiredService<DataCollectorService>();
await dataCollectorService.ApplyConfiguration();
}

public async Task StopAsync(CancellationToken ct)
{
_logger.LogInformation($"Initiating shutdown for module: Monitoring");
await using var scope = _serviceProvider.CreateAsyncScope();
var dataCollectorService = scope.ServiceProvider.GetRequiredService<DataCollectorService>();

await dataCollectorService.Shutdown();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ public interface ICertificateManagementService
public Task<List<CertificateDTO>> GetCertificates();
public Task<CertificateDTO?> GetCertificate(Guid Id);
public Task<CertificateDTO> AddCertificate(string name, string fqdn, KeyAlgorithm algorithm, Guid authorityId);
public Task<CertificateDTO> RenewCertificate(Guid Id);
public Task RenewExpiringCertificates();
public Task RevokeCertificate(Guid Id);
public Task DeleteCertificate(Guid Id);

Expand Down
9 changes: 9 additions & 0 deletions tilework.core/Interfaces/Core/IContainerManager.cs
Original file line number Diff line number Diff line change
@@ -1,14 +1,21 @@
using System.Net;

using Tilework.Core.Enums;
using Tilework.Core.Models;

namespace Tilework.Core.Interfaces;

public interface IContainerManager
{
public Task Initialize();

public Task<List<ContainerNetwork>> ListNetworks();
public Task<ContainerNetwork> CreateNetwork(string name);
public Task DeleteNetwork(string id);

public Task<IPAddress> GetContainerAddress(string id);
public Task<List<ContainerPort>> GetContainerPorts(string id);

public Task<List<Container>> ListContainers(string? module);
public Task<Container> CreateContainer(string name, string image, string module, List<ContainerPort>? ports);
public Task DeleteContainer(string id);
Expand All @@ -17,4 +24,6 @@ public interface IContainerManager
public Task StartContainer(string id);
public Task StopContainer(string id);
public Task KillContainer(string id, UnixSignal signal);

public Task<ContainerCommandResult> ExecuteContainerCommand(string id, string command);
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ public interface ILoadBalancerService
public Task EnableLoadBalancer(Guid Id);
public Task DisableLoadBalancer(Guid Id);



public Task<List<RuleDTO>> GetRules(ApplicationLoadBalancerDTO balancer);
public Task AddRule(ApplicationLoadBalancerDTO balancer, RuleDTO rule);
public Task UpdateRule(ApplicationLoadBalancerDTO balancer, RuleDTO rule);
Expand All @@ -40,6 +42,9 @@ public interface ILoadBalancerService
public Task UpdateTarget(TargetGroupDTO group, TargetDTO target);
public Task RemoveTarget(TargetGroupDTO group, TargetDTO target);

public Task<List<LoadBalancingMonitorData>> GetLoadBalancerMonitoringData(Guid Id, TimeSpan interval, DateTimeOffset start, DateTimeOffset end);

public Task ApplyConfiguration(Guid Id);
public Task ApplyConfiguration();
public Task Shutdown();
}
}
Loading