Add TCP/UDP port mapping support

- Add app_ports table for storing port mappings per app
- Add Port model with CRUD operations
- Add handlers for adding/deleting port mappings
- Add ports section to app detail template
- Update Docker client to configure port bindings when creating containers
- Support both TCP and UDP protocols
This commit is contained in:
2025-12-30 12:11:57 +07:00
parent 4ece7431af
commit bc275f7b9c
9 changed files with 398 additions and 5 deletions

View File

@@ -9,6 +9,7 @@ import (
"log/slog"
"os"
"path/filepath"
"strconv"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/container"
@@ -17,6 +18,7 @@ import (
"github.com/docker/docker/api/types/network"
"github.com/docker/docker/client"
"github.com/docker/docker/pkg/archive"
"github.com/docker/go-connections/nat"
"go.uber.org/fx"
"git.eeqj.de/sneak/upaas/internal/config"
@@ -116,6 +118,7 @@ type CreateContainerOptions struct {
Env map[string]string
Labels map[string]string
Volumes []VolumeMount
Ports []PortMapping
Network string
}
@@ -126,6 +129,37 @@ type VolumeMount struct {
ReadOnly bool
}
// PortMapping represents a port mapping.
type PortMapping struct {
HostPort int
ContainerPort int
Protocol string // "tcp" or "udp"
}
// buildPortConfig converts port mappings to Docker port configuration.
func buildPortConfig(ports []PortMapping) (nat.PortSet, nat.PortMap) {
exposedPorts := make(nat.PortSet)
portBindings := make(nat.PortMap)
for _, p := range ports {
proto := p.Protocol
if proto == "" {
proto = "tcp"
}
containerPort := nat.Port(fmt.Sprintf("%d/%s", p.ContainerPort, proto))
exposedPorts[containerPort] = struct{}{}
portBindings[containerPort] = []nat.PortBinding{
{
HostIP: "0.0.0.0",
HostPort: strconv.Itoa(p.HostPort),
},
}
}
return exposedPorts, portBindings
}
// CreateContainer creates a new container.
func (c *Client) CreateContainer(
ctx context.Context,
@@ -156,16 +190,21 @@ func (c *Client) CreateContainer(
})
}
// Convert ports to exposed ports and port bindings
exposedPorts, portBindings := buildPortConfig(opts.Ports)
// Create container
resp, err := c.docker.ContainerCreate(ctx,
&container.Config{
Image: opts.Image,
Env: envSlice,
Labels: opts.Labels,
Image: opts.Image,
Env: envSlice,
Labels: opts.Labels,
ExposedPorts: exposedPorts,
},
&container.HostConfig{
Mounts: mounts,
NetworkMode: container.NetworkMode(opts.Network),
Mounts: mounts,
PortBindings: portBindings,
NetworkMode: container.NetworkMode(opts.Network),
RestartPolicy: container.RestartPolicy{
Name: container.RestartPolicyUnlessStopped,
},