diff --git a/go.mod b/go.mod index 1c54148..a7f82fd 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,9 @@ require ( connectrpc.com/connect v1.18.1 connectrpc.com/grpchealth v1.3.0 connectrpc.com/grpcreflect v1.3.0 + github.com/aws/aws-sdk-go-v2 v1.32.7 + github.com/aws/aws-sdk-go-v2/config v1.28.7 + github.com/aws/aws-sdk-go-v2/service/s3 v1.71.1 github.com/caarlos0/env/v11 v11.3.1 github.com/cloudflare/cloudflare-go v0.115.0 github.com/docker/docker v27.4.1+incompatible @@ -17,10 +20,10 @@ require ( github.com/pressly/goose/v3 v3.24.1 github.com/traefik/paerser v0.2.1 github.com/traefik/traefik/v3 v3.3.3 - golang.org/x/crypto v0.32.0 - golang.org/x/exp v0.0.0-20250128182459-e0ece0dbea4c - golang.org/x/net v0.34.0 - google.golang.org/protobuf v1.36.4 + golang.org/x/crypto v0.33.0 + golang.org/x/exp v0.0.0-20250210185358-939b2ce775ac + golang.org/x/net v0.35.0 + google.golang.org/protobuf v1.36.5 modernc.org/sqlite v1.34.5 sigs.k8s.io/yaml v1.4.0 ) @@ -28,6 +31,21 @@ require ( require ( github.com/Microsoft/go-winio v0.6.2 // indirect github.com/aws/aws-sdk-go v1.55.6 // indirect + github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.7 // indirect + github.com/aws/aws-sdk-go-v2/credentials v1.17.48 // indirect + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.22 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.26 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.26 // indirect + github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 // indirect + github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.26 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.4.7 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.7 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.7 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.24.8 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.7 // indirect + github.com/aws/aws-sdk-go-v2/service/sts v1.33.3 // indirect + github.com/aws/smithy-go v1.22.1 // indirect github.com/cenkalti/backoff/v4 v4.3.0 // indirect github.com/distribution/reference v0.6.0 // indirect github.com/docker/go-connections v0.5.0 // indirect @@ -46,7 +64,7 @@ require ( github.com/google/go-github/v28 v28.1.1 // indirect github.com/google/go-querystring v1.1.0 // indirect github.com/gorilla/mux v1.8.1 // indirect - github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.0 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1 // indirect github.com/hashicorp/go-version v1.7.0 // indirect github.com/http-wasm/http-wasm-host-go v0.7.0 // indirect github.com/mattn/go-colorable v0.1.14 // indirect @@ -83,12 +101,12 @@ require ( golang.org/x/sys v0.30.0 // indirect golang.org/x/text v0.22.0 // indirect golang.org/x/time v0.10.0 // indirect - golang.org/x/tools v0.29.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20250204164813-702378808489 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20250204164813-702378808489 // indirect + golang.org/x/tools v0.30.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20250212204824-5a70512c5d8b // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250212204824-5a70512c5d8b // indirect google.golang.org/grpc v1.70.0 // indirect gotest.tools/v3 v3.5.1 // indirect - modernc.org/libc v1.61.11 // indirect + modernc.org/libc v1.61.13 // indirect modernc.org/mathutil v1.7.1 // indirect modernc.org/memory v1.8.2 // indirect ) diff --git a/go.sum b/go.sum index 95246e2..a6a63d6 100644 --- a/go.sum +++ b/go.sum @@ -12,6 +12,42 @@ github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERo github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= github.com/aws/aws-sdk-go v1.55.6 h1:cSg4pvZ3m8dgYcgqB97MrcdjUmZ1BeMYKUxMMB89IPk= github.com/aws/aws-sdk-go v1.55.6/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= +github.com/aws/aws-sdk-go-v2 v1.32.7 h1:ky5o35oENWi0JYWUZkB7WYvVPP+bcRF5/Iq7JWSb5Rw= +github.com/aws/aws-sdk-go-v2 v1.32.7/go.mod h1:P5WJBrYqqbWVaOxgH0X/FYYD47/nooaPOZPlQdmiN2U= +github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.7 h1:lL7IfaFzngfx0ZwUGOZdsFFnQ5uLvR0hWqqhyE7Q9M8= +github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.7/go.mod h1:QraP0UcVlQJsmHfioCrveWOC1nbiWUl3ej08h4mXWoc= +github.com/aws/aws-sdk-go-v2/config v1.28.7 h1:GduUnoTXlhkgnxTD93g1nv4tVPILbdNQOzav+Wpg7AE= +github.com/aws/aws-sdk-go-v2/config v1.28.7/go.mod h1:vZGX6GVkIE8uECSUHB6MWAUsd4ZcG2Yq/dMa4refR3M= +github.com/aws/aws-sdk-go-v2/credentials v1.17.48 h1:IYdLD1qTJ0zanRavulofmqut4afs45mOWEI+MzZtTfQ= +github.com/aws/aws-sdk-go-v2/credentials v1.17.48/go.mod h1:tOscxHN3CGmuX9idQ3+qbkzrjVIx32lqDSU1/0d/qXs= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.22 h1:kqOrpojG71DxJm/KDPO+Z/y1phm1JlC8/iT+5XRmAn8= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.22/go.mod h1:NtSFajXVVL8TA2QNngagVZmUtXciyrHOt7xgz4faS/M= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.26 h1:I/5wmGMffY4happ8NOCuIUEWGUvvFp5NSeQcXl9RHcI= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.26/go.mod h1:FR8f4turZtNy6baO0KJ5FJUmXH/cSkI9fOngs0yl6mA= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.26 h1:zXFLuEuMMUOvEARXFUVJdfqZ4bvvSgdGRq/ATcrQxzM= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.26/go.mod h1:3o2Wpy0bogG1kyOPrgkXA8pgIfEEv0+m19O9D5+W8y8= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 h1:VaRN3TlFdd6KxX1x3ILT5ynH6HvKgqdiXoTxAF4HQcQ= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1/go.mod h1:FbtygfRFze9usAadmnGJNc8KsP346kEe+y2/oyhGAGc= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.26 h1:GeNJsIFHB+WW5ap2Tec4K6dzcVTsRbsT1Lra46Hv9ME= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.26/go.mod h1:zfgMpwHDXX2WGoG84xG2H+ZlPTkJUU4YUvx2svLQYWo= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1 h1:iXtILhvDxB6kPvEXgsDhGaZCSC6LQET5ZHSdJozeI0Y= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1/go.mod h1:9nu0fVANtYiAePIBh2/pFUSwtJ402hLnp854CNoDOeE= +github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.4.7 h1:tB4tNw83KcajNAzaIMhkhVI2Nt8fAZd5A5ro113FEMY= +github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.4.7/go.mod h1:lvpyBGkZ3tZ9iSsUIcC2EWp+0ywa7aK3BLT+FwZi+mQ= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.7 h1:8eUsivBQzZHqe/3FE+cqwfH+0p5Jo8PFM/QYQSmeZ+M= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.7/go.mod h1:kLPQvGUmxn/fqiCrDeohwG33bq2pQpGeY62yRO6Nrh0= +github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.7 h1:Hi0KGbrnr57bEHWM0bJ1QcBzxLrL/k2DHvGYhb8+W1w= +github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.7/go.mod h1:wKNgWgExdjjrm4qvfbTorkvocEstaoDl4WCvGfeCy9c= +github.com/aws/aws-sdk-go-v2/service/s3 v1.71.1 h1:aOVVZJgWbaH+EJYPvEgkNhCEbXXvH7+oML36oaPK3zE= +github.com/aws/aws-sdk-go-v2/service/s3 v1.71.1/go.mod h1:r+xl5yzMk9083rMR+sJ5TYj9Tihvf/l1oxzZXDgGj2Q= +github.com/aws/aws-sdk-go-v2/service/sso v1.24.8 h1:CvuUmnXI7ebaUAhbJcDy9YQx8wHR69eZ9I7q5hszt/g= +github.com/aws/aws-sdk-go-v2/service/sso v1.24.8/go.mod h1:XDeGv1opzwm8ubxddF0cgqkZWsyOtw4lr6dxwmb6YQg= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.7 h1:F2rBfNAL5UyswqoeWv9zs74N/NanhK16ydHW1pahX6E= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.7/go.mod h1:JfyQ0g2JG8+Krq0EuZNnRwX0mU0HrwY/tG6JNfcqh4k= +github.com/aws/aws-sdk-go-v2/service/sts v1.33.3 h1:Xgv/hyNgvLda/M9l9qxXc4UFSgppnRczLxlMs5Ae/QY= +github.com/aws/aws-sdk-go-v2/service/sts v1.33.3/go.mod h1:5Gn+d+VaaRgsjewpMvGazt0WfcFO+Md4wLOuBfGR9Bc= +github.com/aws/smithy-go v1.22.1 h1:/HPHZQ0g7f4eUeK6HKglFz8uwVfZKgoI25rb/J+dnro= +github.com/aws/smithy-go v1.22.1/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg= github.com/caarlos0/env/v11 v11.3.1 h1:cArPWC15hWmEt+gWk7YBi7lEXTXCvpaSdCiZE2X5mCA= github.com/caarlos0/env/v11 v11.3.1/go.mod h1:qupehSf/Y0TUTsxKywqRt/vJjN5nz6vauiYEUUr8P4U= github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= @@ -77,8 +113,8 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.0 h1:VD1gqscl4nYs1YxVuSdemTrSgTKrwOWDK0FVFMqm+Cg= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.0/go.mod h1:4EgsQoS4TOhJizV+JTFg40qx1Ofh3XmXEQNBpgvNT40= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1 h1:e9Rjr40Z98/clHv5Yg79Is0NtosR5LXRvdr7o/6NwbA= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1/go.mod h1:tIxuGz/9mpox++sgp9fJjHO0+q1X9/UOWd798aAm22M= github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY= github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/http-wasm/http-wasm-host-go v0.7.0 h1:+1KrRyOO6tWiDB24QrtSYyDmzFLBBs3jioKaUT0mq1c= @@ -194,10 +230,10 @@ go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN8 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc= -golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= -golang.org/x/exp v0.0.0-20250128182459-e0ece0dbea4c h1:KL/ZBHXgKGVmuZBZ01Lt57yE5ws8ZPSkkihmEyq7FXc= -golang.org/x/exp v0.0.0-20250128182459-e0ece0dbea4c/go.mod h1:tujkw807nyEEAamNbDrEGzRav+ilXA7PCRAd6xsmwiU= +golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus= +golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M= +golang.org/x/exp v0.0.0-20250210185358-939b2ce775ac h1:l5+whBCLH3iH2ZNHYLbAe58bo7yrN4mVcnkHDYz5vvs= +golang.org/x/exp v0.0.0-20250210185358-939b2ce775ac/go.mod h1:hH+7mtFmImwwcMvScyxUhjuVHR3HGaDPMn9rMSUUbxo= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.23.0 h1:Zb7khfcRGKk+kqfxFaP5tZqCnDZMjC5VtUBs87Hr6QM= @@ -207,8 +243,8 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0= -golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k= +golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8= +golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -234,21 +270,21 @@ golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGm golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.29.0 h1:Xx0h3TtM9rzQpQuR4dKLrdglAmCEN5Oi+P74JdhdzXE= -golang.org/x/tools v0.29.0/go.mod h1:KMQVMRsVxU6nHCFXrBPhDB8XncLNLM0lIy/F14RP588= +golang.org/x/tools v0.30.0 h1:BgcpHewrV5AUp2G9MebG4XPFI1E2W41zU1SaqVA9vJY= +golang.org/x/tools v0.30.0/go.mod h1:c347cR/OJfw5TI+GfX7RUPNMdDRRbjvYTS0jPyvsVtY= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/genproto/googleapis/api v0.0.0-20250204164813-702378808489 h1:fCuMM4fowGzigT89NCIsW57Pk9k2D12MMi2ODn+Nk+o= -google.golang.org/genproto/googleapis/api v0.0.0-20250204164813-702378808489/go.mod h1:iYONQfRdizDB8JJBybql13nArx91jcUk7zCXEsOofM4= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250204164813-702378808489 h1:5bKytslY8ViY0Cj/ewmRtrWHW64bNF03cAatUUFCdFI= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250204164813-702378808489/go.mod h1:8BS3B93F/U1juMFq9+EDk+qOT5CO1R9IzXxG3PTqiRk= +google.golang.org/genproto/googleapis/api v0.0.0-20250212204824-5a70512c5d8b h1:i+d0RZa8Hs2L/MuaOQYI+krthcxdEbEM2N+Tf3kJ4zk= +google.golang.org/genproto/googleapis/api v0.0.0-20250212204824-5a70512c5d8b/go.mod h1:iYONQfRdizDB8JJBybql13nArx91jcUk7zCXEsOofM4= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250212204824-5a70512c5d8b h1:FQtJ1MxbXoIIrZHZ33M+w5+dAP9o86rgpjoKr/ZmT7k= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250212204824-5a70512c5d8b/go.mod h1:8BS3B93F/U1juMFq9+EDk+qOT5CO1R9IzXxG3PTqiRk= google.golang.org/grpc v1.70.0 h1:pWFv03aZoHzlRKHWicjsZytKAiYCtNS0dHbXnIdq7jQ= google.golang.org/grpc v1.70.0/go.mod h1:ofIJqVKDXx/JiXrwr2IG4/zwdH9txy3IlF40RmcJSQw= -google.golang.org/protobuf v1.36.4 h1:6A3ZDJHn/eNqc1i+IdefRzy/9PokBTPvcqMySR7NNIM= -google.golang.org/protobuf v1.36.4/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM= +google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= @@ -258,14 +294,14 @@ gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU= gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU= modernc.org/cc/v4 v4.24.4 h1:TFkx1s6dCkQpd6dKurBNmpo+G8Zl4Sq/ztJ+2+DEsh0= modernc.org/cc/v4 v4.24.4/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0= -modernc.org/ccgo/v4 v4.23.15 h1:wFDan71KnYqeHz4eF63vmGE6Q6Pc0PUGDpP0PRMYjDc= -modernc.org/ccgo/v4 v4.23.15/go.mod h1:nJX30dks/IWuBOnVa7VRii9Me4/9TZ1SC9GNtmARTy0= +modernc.org/ccgo/v4 v4.23.16 h1:Z2N+kk38b7SfySC1ZkpGLN2vthNJP1+ZzGZIlH7uBxo= +modernc.org/ccgo/v4 v4.23.16/go.mod h1:nNma8goMTY7aQZQNTyN9AIoJfxav4nvTnvKThAeMDdo= modernc.org/fileutil v1.3.0 h1:gQ5SIzK3H9kdfai/5x41oQiKValumqNTDXMvKo62HvE= modernc.org/fileutil v1.3.0/go.mod h1:XatxS8fZi3pS8/hKG2GH/ArUogfxjpEKs3Ku3aK4JyQ= -modernc.org/gc/v2 v2.6.2 h1:YBXi5Kqp6aCK3fIxwKQ3/fErvawVKwjOLItxj1brGds= -modernc.org/gc/v2 v2.6.2/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito= -modernc.org/libc v1.61.11 h1:6sZG8uB6EMMG7iTLPTndi8jyTdgAQNIeLGjCFICACZw= -modernc.org/libc v1.61.11/go.mod h1:HHX+srFdn839oaJRd0W8hBM3eg+mieyZCAjWwB08/nM= +modernc.org/gc/v2 v2.6.3 h1:aJVhcqAte49LF+mGveZ5KPlsp4tdGdAOT4sipJXADjw= +modernc.org/gc/v2 v2.6.3/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito= +modernc.org/libc v1.61.13 h1:3LRd6ZO1ezsFiX1y+bHd1ipyEHIJKvuprv0sLTBwLW8= +modernc.org/libc v1.61.13/go.mod h1:8F/uJWL/3nNil0Lgt1Dpz+GgkApWh04N3el3hxJcA6E= modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU= modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg= modernc.org/memory v1.8.2 h1:cL9L4bcoAObu4NkxOlKWBWtNHIsnnACGF/TbqQ6sbcI= diff --git a/internal/api/server/server.go b/internal/api/server/server.go index 057ffe3..9f15bfb 100644 --- a/internal/api/server/server.go +++ b/internal/api/server/server.go @@ -56,10 +56,7 @@ func (s *Server) Start(ctx context.Context) error { // Start server in a goroutine go func() { - slog.Info("Server starting", - "host", host, - "port", port, - ) + slog.Info("Server starting", "host", host, "port", port) if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed { serverErr <- err } diff --git a/internal/app/config.go b/internal/app/config.go index 4b1ebdc..fec3d1c 100644 --- a/internal/app/config.go +++ b/internal/app/config.go @@ -69,6 +69,7 @@ type BackupConfig struct { type BackgroundJobs struct { Traefik int64 `env:"BACKGROUND_JOBS_TRAEFIK" envDefault:"20"` DNS int64 `env:"BACKGROUND_JOBS_DNS" envDefault:"300"` + Agent int64 `env:"BACKGROUND_JOBS_AGENT" envDefault:"180"` } func ReadConfig() (*Config, error) { diff --git a/internal/backup/backup.go b/internal/backup/backup.go index 76fdb6e..4cb9f02 100644 --- a/internal/backup/backup.go +++ b/internal/backup/backup.go @@ -13,26 +13,13 @@ import ( "github.com/MizuchiLabs/mantrae/internal/app" "github.com/MizuchiLabs/mantrae/internal/db" + "github.com/MizuchiLabs/mantrae/internal/storage" ) -// StorageBackend defines interface for different storage solutions -type StorageBackend interface { - Store(ctx context.Context, name string, data io.Reader) error - Retrieve(ctx context.Context, name string) (io.ReadCloser, error) - List(ctx context.Context) ([]BackupFile, error) - Delete(ctx context.Context, name string) error -} - -type BackupFile struct { - Name string `json:"name,omitempty"` - Timestamp time.Time `json:"timestamp,omitempty"` - Size int64 `json:"size,omitempty"` -} - type BackupManager struct { Conn *db.Connection Config *app.BackupConfig - Backend StorageBackend + Backend storage.Backend stopChan chan struct{} waitGroup sync.WaitGroup mu sync.Mutex @@ -41,7 +28,7 @@ type BackupManager struct { func NewManager( conn *db.Connection, config app.BackupConfig, - backend StorageBackend, + backend storage.Backend, ) *BackupManager { return &BackupManager{ Conn: conn, diff --git a/internal/backup/s3.go b/internal/backup/s3.go deleted file mode 100644 index c3d36bf..0000000 --- a/internal/backup/s3.go +++ /dev/null @@ -1,15 +0,0 @@ -package backup - -// type S3Storage struct { -// bucket string -// prefix string -// s3Client *s3.Client -// } -// -// func NewS3Storage(bucket, prefix string, s3Client *s3.Client) *S3Storage { -// return &S3Storage{ -// bucket: bucket, -// prefix: prefix, -// s3Client: s3Client, -// } -// } diff --git a/internal/config/setup.go b/internal/config/setup.go index 2b6232e..a94cbf4 100644 --- a/internal/config/setup.go +++ b/internal/config/setup.go @@ -11,6 +11,7 @@ import ( "github.com/MizuchiLabs/mantrae/internal/backup" "github.com/MizuchiLabs/mantrae/internal/db" "github.com/MizuchiLabs/mantrae/internal/source" + "github.com/MizuchiLabs/mantrae/internal/storage" "github.com/MizuchiLabs/mantrae/internal/util" "github.com/lmittmann/tint" "golang.org/x/crypto/bcrypt" @@ -41,8 +42,7 @@ func Setup(ctx context.Context) (*App, error) { return nil, err } - // Setup backup manager - storage, err := backup.NewLocalStorage(config.Backup.BackupPath) + storage, err := storage.NewLocalStorage(config.Backup.BackupPath) if err != nil { return nil, err } @@ -192,6 +192,13 @@ func (a *App) setDefaultProfile(ctx context.Context) error { return nil } + if profile.Url == a.Config.Traefik.URL && + profile.Username == &a.Config.Traefik.Username && + profile.Password == &a.Config.Traefik.Password && + profile.Tls == a.Config.Traefik.TLS { + return nil + } + if err := q.UpdateProfile(ctx, db.UpdateProfileParams{ ID: profile.ID, Name: a.Config.Traefik.Profile, diff --git a/internal/config/tasks.go b/internal/config/tasks.go index d11e298..2980daa 100644 --- a/internal/config/tasks.go +++ b/internal/config/tasks.go @@ -7,15 +7,14 @@ import ( "github.com/MizuchiLabs/mantrae/internal/dns" "github.com/MizuchiLabs/mantrae/internal/traefik" + "github.com/MizuchiLabs/mantrae/internal/util" ) func (a *App) setupBackgroundJobs(ctx context.Context) { slog.Info("Starting background tasks...") go a.traefikSync(ctx) go a.syncDNS(ctx) - // go sslCheck(ctx) - // go cleanupAgents(ctx) - // go cleanupRouters(ctx) + go a.cleanupAgents(ctx) } // traefikSync periodically syncs the Traefik configuration @@ -54,151 +53,80 @@ func (a *App) syncDNS(ctx context.Context) { } } -// func sslCheck(ctx context.Context) { -// ticker := time.NewTicker(time.Second * time.Duration(util.App.SSLInterval)) -// defer ticker.Stop() +func (a *App) cleanupAgents(ctx context.Context) { + ticker := time.NewTicker(time.Second * time.Duration(a.Config.Background.Agent)) + defer ticker.Stop() -// for { -// select { -// case <-ctx.Done(): -// return -// case <-ticker.C: -// // Fetch new router list -// routers, err := db.Query.ListRouters(context.Background()) -// if err != nil { -// slog.Error("Failed to get routers", "error", err) -// } + for { + select { + case <-ctx.Done(): + return + case <-ticker.C: + q := a.Conn.GetQuery() + enabled, err := q.GetSetting(ctx, "agent_cleanup_enabled") + if err != nil { + slog.Error("failed to get agent_cleanup_enabled", "error", err) + return + } -// for _, router := range routers { -// router.SSLCheck() -// } -// } -// } -// } + if enabled.Value != "true" { + return + } -// func cleanupAgents(ctx context.Context) { -// enabled, err := db.Query.GetSetting(context.Background(), "agent-cleanup-enabled") -// if err != nil { -// slog.Error("failed to get agent cleanup timeout", "error", err) -// return -// } + // Timeout to delete old agents + timeout, err := q.GetSetting(ctx, "agent_cleanup_interval") + if err != nil { + slog.Error("failed to get agent_cleanup_interval", "error", err) + return + } -// if enabled.Value != "true" { -// return -// } + timeoutDuration, err := time.ParseDuration(timeout.Value) + if err != nil { + slog.Error("failed to parse agent_cleanup_interval", "error", err) + return + } -// // Timeout to delete old agents -// timeout, err := db.Query.GetSetting(context.Background(), "agent-cleanup-timeout") -// if err != nil { -// slog.Error("failed to get agent cleanup timeout", "error", err) -// return -// } + now := time.Now() + agents, err := q.ListAgents(ctx) + if err != nil { + slog.Error("failed to list agents", "error", err) + return + } -// timeoutDuration, err := time.ParseDuration(timeout.Value) -// if err != nil { -// slog.Error("failed to parse timeout cleanup duration", "error", err) -// } + for _, agent := range agents { + if agent.UpdatedAt == nil || agent.Hostname == nil { + continue + } -// ticker := time.NewTicker(timeoutDuration) -// defer ticker.Stop() - -// for { -// select { -// case <-ctx.Done(): -// return -// case <-ticker.C: -// now := time.Now() -// agents, err := db.Query.ListAgents(context.Background()) -// if err != nil { -// slog.Error("failed to query disconnected agents", "error", err) -// continue -// } - -// for _, agent := range agents { -// if agent.LastSeen == nil { -// continue -// } - -// if now.Sub(*agent.LastSeen) > timeoutDuration { -// if err := db.Query.DeleteAgent(context.Background(), agent.ID); err != nil { -// slog.Error( -// "failed to delete disconnected agent", -// "id", -// agent.ID, -// "error", -// err, -// ) -// } else { -// slog.Info("Deleted disconnected agent", "id", agent.ID) -// util.Broadcast <- util.EventMessage{ -// Type: "agent_updated", -// Message: "Deleted disconnected agent", -// } -// } - -// // Delete all connected routers -// routers, err := db.Query.ListRoutersByAgentID(context.Background(), &agent.ID) -// if err != nil { -// slog.Error("Failed to get routers", "error", err) -// continue -// } -// for _, router := range routers { -// if err := db.Query.DeleteRouter(context.Background(), router.ID); err != nil { -// slog.Error("Failed to delete router", "id", router.ID, "error", err) -// return -// } -// } -// } -// } -// } -// } -// } - -// cleanupRouters periodically deletes routers from offline/deleted agents -// func (a *App) cleanupRouters(ctx context.Context) { -// q := db.New(a.DB) -// // Timeout to delete old agents -// timeout, err := q.GetSetting(context.Background(), "agent-cleanup-timeout") -// if err != nil { -// slog.Error("failed to get agent cleanup timeout", "error", err) -// return -// } -// -// timeoutDuration, err := time.ParseDuration(timeout.Value) -// if err != nil { -// slog.Error("failed to parse timeout cleanup duration", "error", err) -// } -// -// ticker := time.NewTicker(timeoutDuration) -// defer ticker.Stop() -// -// for { -// select { -// case <-ctx.Done(): -// return -// case <-ticker.C: -// q := db.New(a.DB) -// -// profiles, err := q.ListProfiles(ctx) -// if err != nil { -// slog.Error("failed to query disconnected agents", "error", err) -// continue -// } -// -// for _, profile := range profiles { -// config, err := q.GetTraefikConfigBySource(ctx, db.GetTraefikConfigBySourceParams{ -// ProfileID: profile.ID, -// Source: source.Agent, -// }) -// if err != nil { -// slog.Error("failed to get agent config", "error", err) -// continue -// } -// if config.Config == nil { -// continue -// } -// // TODO -// } -// } -// } -// } + if now.Sub(*agent.UpdatedAt) > timeoutDuration { + if err := q.DeleteTraefikConfigByAgent(ctx, &agent.ID); err != nil { + slog.Error( + "failed to delete agent config", + "id", + agent.ID, + "error", + err, + ) + continue + } + if err := q.DeleteAgent(ctx, agent.ID); err != nil { + slog.Error( + "failed to delete disconnected agent", + "id", + agent.ID, + "error", + err, + ) + continue + } else { + slog.Info("Deleted disconnected agent", "id", agent.ID) + util.Broadcast <- util.EventMessage{ + Type: util.EventTypeDelete, + Message: "agent", + } + } + } + } + } + } +} diff --git a/internal/db/queries/agents.sql b/internal/db/queries/agents.sql index 19dd290..a7ee2b1 100644 --- a/internal/db/queries/agents.sql +++ b/internal/db/queries/agents.sql @@ -1,8 +1,8 @@ -- name: CreateAgent :exec INSERT INTO - agents (id, profile_id, token) + agents (id, profile_id, token, created_at) VALUES - (?, ?, ?); + (?, ?, ?, CURRENT_TIMESTAMP); -- name: GetAgent :one SELECT diff --git a/internal/backup/local.go b/internal/storage/local.go similarity index 91% rename from internal/backup/local.go rename to internal/storage/local.go index ddbfc14..1305204 100644 --- a/internal/backup/local.go +++ b/internal/storage/local.go @@ -1,4 +1,4 @@ -package backup +package storage import ( "context" @@ -40,8 +40,8 @@ func (ls *LocalStorage) Retrieve(ctx context.Context, name string) (io.ReadClose return os.Open(path) } -func (ls *LocalStorage) List(ctx context.Context) ([]BackupFile, error) { - var files []BackupFile +func (ls *LocalStorage) List(ctx context.Context) ([]StoredFile, error) { + var files []StoredFile entries, err := os.ReadDir(ls.basePath) if err != nil { return nil, fmt.Errorf("failed to read storage directory: %w", err) @@ -52,7 +52,7 @@ func (ls *LocalStorage) List(ctx context.Context) ([]BackupFile, error) { if err != nil { continue } - files = append(files, BackupFile{ + files = append(files, StoredFile{ Name: entry.Name(), Timestamp: info.ModTime(), Size: info.Size(), diff --git a/internal/storage/s3.go b/internal/storage/s3.go new file mode 100644 index 0000000..8346be2 --- /dev/null +++ b/internal/storage/s3.go @@ -0,0 +1,126 @@ +package storage + +import ( + "context" + "fmt" + "io" + "sort" + "time" + + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/config" + "github.com/aws/aws-sdk-go-v2/service/s3" +) + +// S3Storage implements StorageBackend for AWS S3 +type S3Storage struct { + client *s3.Client + bucket string +} + +// NewS3Storage creates a new S3Storage instance +func NewS3Storage( + ctx context.Context, + bucket string, + awsConfig aws.Config, +) (*S3Storage, error) { + if bucket == "" { + return nil, fmt.Errorf("bucket name cannot be empty") + } + + var cfg aws.Config + var err error + + if awsConfig.Region == "" { + cfg, err = config.LoadDefaultConfig(ctx) + if err != nil { + return nil, fmt.Errorf("failed to load AWS config: %w", err) + } + } else { + cfg = awsConfig + } + + return &S3Storage{ + bucket: bucket, + client: s3.NewFromConfig(cfg), + }, nil +} + +func (s *S3Storage) Store(ctx context.Context, name string, data io.Reader) error { + _, err := s.client.PutObject(ctx, &s3.PutObjectInput{ + Bucket: aws.String(s.bucket), + Key: aws.String(name), + Body: data, + }) + if err != nil { + return fmt.Errorf("failed to store object in S3: %w", err) + } + return nil +} + +func (s *S3Storage) Retrieve(ctx context.Context, name string) (io.ReadCloser, error) { + output, err := s.client.GetObject(ctx, &s3.GetObjectInput{ + Bucket: aws.String(s.bucket), + Key: aws.String(name), + }) + if err != nil { + return nil, fmt.Errorf("failed to retrieve object from S3: %w", err) + } + return output.Body, nil +} + +// List lists the files in S3 +func (s *S3Storage) List(ctx context.Context) ([]StoredFile, error) { + var files []StoredFile + var nextToken *string + + for { + output, err := s.client.ListObjectsV2(ctx, &s3.ListObjectsV2Input{ + Bucket: aws.String(s.bucket), + ContinuationToken: nextToken, + }) + if err != nil { + return nil, fmt.Errorf("failed to list objects from S3: %w", err) + } + + for _, object := range output.Contents { + // Safely dereference pointers. + key := "" + if object.Key != nil { + key = *object.Key + } + var ts time.Time + if object.LastModified != nil { + ts = *object.LastModified + } + files = append(files, StoredFile{ + Name: key, + Timestamp: ts, + Size: *object.Size, + }) + } + + nextToken = output.NextContinuationToken + + if nextToken == nil { + break + } + } + + sort.Slice(files, func(i, j int) bool { + return files[i].Timestamp.After(files[j].Timestamp) + }) + + return files, nil +} + +func (s *S3Storage) Delete(ctx context.Context, name string) error { + _, err := s.client.DeleteObject(ctx, &s3.DeleteObjectInput{ + Bucket: aws.String(s.bucket), + Key: aws.String(name), + }) + if err != nil { + return fmt.Errorf("failed to delete object from S3: %w", err) + } + return nil +} diff --git a/internal/storage/types.go b/internal/storage/types.go new file mode 100644 index 0000000..82b8890 --- /dev/null +++ b/internal/storage/types.go @@ -0,0 +1,21 @@ +package storage + +import ( + "context" + "io" + "time" +) + +// StorageBackend defines interface for different storage solutions +type Backend interface { + Store(ctx context.Context, name string, data io.Reader) error + Retrieve(ctx context.Context, name string) (io.ReadCloser, error) + List(ctx context.Context) ([]StoredFile, error) + Delete(ctx context.Context, name string) error +} + +type StoredFile struct { + Name string `json:"name,omitempty"` + Timestamp time.Time `json:"timestamp,omitempty"` + Size int64 `json:"size,omitempty"` +}