Compare commits

...

6 Commits

Author SHA1 Message Date
GitHub
f79c3f6ca1 docs: regenerate readme sponsors list 2025-10-10 12:48:34 +00:00
Stavros
5a4855c12c refactor: move docker connection check to start up 2025-10-10 15:45:04 +03:00
Stavros
05d4dbd68e fix: ensure data directory exists on docker image 2025-10-10 15:34:09 +03:00
Stavros
ae8347fd28 fix: use docker meta flavors for handling latest, prefix and suffix 2025-10-08 23:11:25 +03:00
CzBiX
76f2014444 feat: add http cache for static files (#395)
* feat: add http cache for static files

fix #392

* minor typo fix
2025-10-08 18:58:22 +03:00
Stavros
5b7bda3378 chore: use bun frozen lockfile for builds 2025-10-08 18:56:42 +03:00
9 changed files with 69 additions and 32 deletions

View File

@@ -23,7 +23,7 @@ jobs:
- name: Install frontend dependencies
run: |
cd frontend
bun install
bun install --frozen-lockfile
- name: Set version
run: |

View File

@@ -66,7 +66,7 @@ jobs:
- name: Install frontend dependencies
run: |
cd frontend
bun install
bun install --frozen-lockfile
- name: Install backend dependencies
run: |
@@ -112,7 +112,7 @@ jobs:
- name: Install frontend dependencies
run: |
cd frontend
bun install
bun install --frozen-lockfile
- name: Install backend dependencies
run: |
@@ -396,6 +396,8 @@ jobs:
uses: docker/metadata-action@v5
with:
images: ghcr.io/${{ github.repository_owner }}/tinyauth
flavor: |
latest=false
tags: |
type=raw,nightly
@@ -433,6 +435,8 @@ jobs:
uses: docker/metadata-action@v5
with:
images: ghcr.io/${{ github.repository_owner }}/tinyauth
flavor: |
latest=false
tags: |
type=raw,nightly-distroless

View File

@@ -44,7 +44,7 @@ jobs:
- name: Install frontend dependencies
run: |
cd frontend
bun install
bun install --frozen-lockfile
- name: Install backend dependencies
run: |
@@ -87,7 +87,7 @@ jobs:
- name: Install frontend dependencies
run: |
cd frontend
bun install
bun install --frozen-lockfile
- name: Install backend dependencies
run: |
@@ -359,10 +359,13 @@ jobs:
uses: docker/metadata-action@v5
with:
images: ghcr.io/${{ github.repository_owner }}/tinyauth
flavor: |
latest=true
prefix=v,onlatest=false
tags: |
type=semver,pattern={{version}},prefix=v
type=semver,pattern={{major}},prefix=v
type=semver,pattern={{major}}.{{minor}},prefix=v
type=semver,pattern={{version}}
type=semver,pattern={{major}}
type=semver,pattern={{major}}.{{minor}}
- name: Create manifest list and push
working-directory: ${{ runner.temp }}/digests
@@ -398,10 +401,14 @@ jobs:
uses: docker/metadata-action@v5
with:
images: ghcr.io/${{ github.repository_owner }}/tinyauth
flavor: |
latest=false
prefix=v,onlatest=false
suffix=-distroless,onlatest=false
tags: |
type=semver,pattern={{version}},prefix=v,suffix=-distroless
type=semver,pattern={{major}},prefix=v,suffix=-distroless
type=semver,pattern={{major}}.{{minor}},prefix=v,suffix=-distroless
type=semver,pattern={{version}}
type=semver,pattern={{major}}
type=semver,pattern={{major}}.{{minor}}
- name: Create manifest list and push
working-directory: ${{ runner.temp }}/digests

View File

@@ -6,7 +6,7 @@ WORKDIR /frontend
COPY ./frontend/package.json ./
COPY ./frontend/bun.lock ./
RUN bun install
RUN bun install --frozen-lockfile
COPY ./frontend/public ./public
COPY ./frontend/src ./src
@@ -47,6 +47,8 @@ WORKDIR /tinyauth
COPY --from=builder /tinyauth/tinyauth ./
RUN mkdir -p /data
EXPOSE 3000
VOLUME ["/data"]

View File

@@ -6,7 +6,7 @@ WORKDIR /frontend
COPY ./frontend/package.json ./
COPY ./frontend/bun.lock ./
RUN bun install
RUN bun install --frozen-lockfile
COPY ./frontend/public ./public
COPY ./frontend/src ./src
@@ -38,6 +38,8 @@ COPY ./cmd ./cmd
COPY ./internal ./internal
COPY --from=frontend-builder /frontend/dist ./internal/assets/dist
RUN mkdir -p /data
RUN CGO_ENABLED=0 go build -ldflags "-s -w -X tinyauth/internal/config.Version=${VERSION} -X tinyauth/internal/config.CommitHash=${COMMIT_HASH} -X tinyauth/internal/config.BuildTimestamp=${BUILD_TIMESTAMP}"
# Runner
@@ -47,6 +49,9 @@ WORKDIR /tinyauth
COPY --from=builder /tinyauth/tinyauth ./
# Since it's distroless, we need to copy the data directory from the builder stage
COPY --from=builder /tinyauth/data /data
EXPOSE 3000
VOLUME ["/data"]

View File

@@ -53,7 +53,7 @@ Tinyauth is licensed under the GNU General Public License v3.0. TL;DR — You ma
A big thank you to the following people for providing me with more coffee:
<!-- sponsors --><a href="https://github.com/erwinkramer"><img src="https:&#x2F;&#x2F;github.com&#x2F;erwinkramer.png" width="64px" alt="User avatar: erwinkramer" /></a>&nbsp;&nbsp;<a href="https://github.com/nicotsx"><img src="https:&#x2F;&#x2F;github.com&#x2F;nicotsx.png" width="64px" alt="User avatar: nicotsx" /></a>&nbsp;&nbsp;<a href="https://github.com/SimpleHomelab"><img src="https:&#x2F;&#x2F;github.com&#x2F;SimpleHomelab.png" width="64px" alt="User avatar: SimpleHomelab" /></a>&nbsp;&nbsp;<a href="https://github.com/jmadden91"><img src="https:&#x2F;&#x2F;github.com&#x2F;jmadden91.png" width="64px" alt="User avatar: jmadden91" /></a>&nbsp;&nbsp;<a href="https://github.com/tribor"><img src="https:&#x2F;&#x2F;github.com&#x2F;tribor.png" width="64px" alt="User avatar: tribor" /></a>&nbsp;&nbsp;<a href="https://github.com/eliasbenb"><img src="https:&#x2F;&#x2F;github.com&#x2F;eliasbenb.png" width="64px" alt="User avatar: eliasbenb" /></a>&nbsp;&nbsp;<a href="https://github.com/afunworm"><img src="https:&#x2F;&#x2F;github.com&#x2F;afunworm.png" width="64px" alt="User avatar: afunworm" /></a>&nbsp;&nbsp;<!-- sponsors -->
<!-- sponsors --><a href="https://github.com/erwinkramer"><img src="https:&#x2F;&#x2F;github.com&#x2F;erwinkramer.png" width="64px" alt="User avatar: erwinkramer" /></a>&nbsp;&nbsp;<a href="https://github.com/nicotsx"><img src="https:&#x2F;&#x2F;github.com&#x2F;nicotsx.png" width="64px" alt="User avatar: nicotsx" /></a>&nbsp;&nbsp;<a href="https://github.com/SimpleHomelab"><img src="https:&#x2F;&#x2F;github.com&#x2F;SimpleHomelab.png" width="64px" alt="User avatar: SimpleHomelab" /></a>&nbsp;&nbsp;<a href="https://github.com/jmadden91"><img src="https:&#x2F;&#x2F;github.com&#x2F;jmadden91.png" width="64px" alt="User avatar: jmadden91" /></a>&nbsp;&nbsp;<a href="https://github.com/tribor"><img src="https:&#x2F;&#x2F;github.com&#x2F;tribor.png" width="64px" alt="User avatar: tribor" /></a>&nbsp;&nbsp;<a href="https://github.com/eliasbenb"><img src="https:&#x2F;&#x2F;github.com&#x2F;eliasbenb.png" width="64px" alt="User avatar: eliasbenb" /></a>&nbsp;&nbsp;<a href="https://github.com/afunworm"><img src="https:&#x2F;&#x2F;github.com&#x2F;afunworm.png" width="64px" alt="User avatar: afunworm" /></a>&nbsp;&nbsp;<a href="https://github.com/chip-well"><img src="https:&#x2F;&#x2F;github.com&#x2F;chip-well.png" width="64px" alt="User avatar: chip-well" /></a>&nbsp;&nbsp;<a href="https://github.com/Lancelot-Enguerrand"><img src="https:&#x2F;&#x2F;github.com&#x2F;Lancelot-Enguerrand.png" width="64px" alt="User avatar: Lancelot-Enguerrand" /></a>&nbsp;&nbsp;<!-- sponsors -->
## Acknowledgements

View File

@@ -2,7 +2,7 @@ root = "/tinyauth"
tmp_dir = "tmp"
[build]
pre_cmd = ["mkdir -p internal/assets/dist", "echo 'backend running' > internal/assets/dist/index.html", "go install github.com/go-delve/delve/cmd/dlv@v1.25.0"]
pre_cmd = ["mkdir -p internal/assets/dist", "mkdir -p /data", "echo 'backend running' > internal/assets/dist/index.html", "go install github.com/go-delve/delve/cmd/dlv@v1.25.0"]
cmd = "CGO_ENABLED=0 go build -gcflags=\"all=-N -l\" -o tmp/tinyauth ."
bin = "/go/bin/dlv --listen :4000 --headless=true --api-version=2 --accept-multiclient --log=true exec tmp/tinyauth --continue --check-go-version=false"
include_ext = ["go"]

View File

@@ -1,10 +1,12 @@
package middleware
import (
"fmt"
"io/fs"
"net/http"
"os"
"strings"
"time"
"tinyauth/internal/assets"
"github.com/gin-gonic/gin"
@@ -27,14 +29,16 @@ func (m *UIMiddleware) Init() error {
}
m.uiFs = ui
m.uiFileServer = http.FileServer(http.FS(ui))
m.uiFileServer = http.FileServerFS(ui)
return nil
}
func (m *UIMiddleware) Middleware() gin.HandlerFunc {
return func(c *gin.Context) {
switch strings.Split(c.Request.URL.Path, "/")[1] {
path := strings.TrimPrefix(c.Request.URL.Path, "/")
switch strings.SplitN(path, "/", 2)[0] {
case "api":
c.Next()
return
@@ -42,12 +46,19 @@ func (m *UIMiddleware) Middleware() gin.HandlerFunc {
c.Next()
return
default:
_, err := fs.Stat(m.uiFs, strings.TrimPrefix(c.Request.URL.Path, "/"))
_, err := fs.Stat(m.uiFs, path)
// Enough for one authentication flow
maxAge := 15 * time.Minute
if os.IsNotExist(err) {
c.Request.URL.Path = "/"
} else if strings.HasPrefix(path, "assets/") {
// assets are named with a hash and can be cached for a long time
maxAge = 30 * 24 * time.Hour
}
c.Writer.Header().Set("Cache-Control", fmt.Sprintf("public, max-age=%d", int(maxAge.Seconds())))
m.uiFileServer.ServeHTTP(c.Writer, c.Request)
c.Abort()
return

View File

@@ -12,8 +12,9 @@ import (
)
type DockerService struct {
client *client.Client
context context.Context
client *client.Client
context context.Context
isConnected bool
}
func NewDockerService() *DockerService {
@@ -31,10 +32,24 @@ func (docker *DockerService) Init() error {
docker.client = client
docker.context = ctx
_, err = docker.client.Ping(docker.context)
if err != nil {
log.Debug().Err(err).Msg("Docker not connected")
docker.isConnected = false
docker.client = nil
docker.context = nil
return nil
}
docker.isConnected = true
log.Debug().Msg("Docker connected")
return nil
}
func (docker *DockerService) GetContainers() ([]container.Summary, error) {
func (docker *DockerService) getContainers() ([]container.Summary, error) {
containers, err := docker.client.ContainerList(docker.context, container.ListOptions{})
if err != nil {
return nil, err
@@ -42,7 +57,7 @@ func (docker *DockerService) GetContainers() ([]container.Summary, error) {
return containers, nil
}
func (docker *DockerService) InspectContainer(containerId string) (container.InspectResponse, error) {
func (docker *DockerService) inspectContainer(containerId string) (container.InspectResponse, error) {
inspect, err := docker.client.ContainerInspect(docker.context, containerId)
if err != nil {
return container.InspectResponse{}, err
@@ -50,26 +65,19 @@ func (docker *DockerService) InspectContainer(containerId string) (container.Ins
return inspect, nil
}
func (docker *DockerService) DockerConnected() bool {
_, err := docker.client.Ping(docker.context)
return err == nil
}
func (docker *DockerService) GetLabels(appDomain string) (config.App, error) {
isConnected := docker.DockerConnected()
if !isConnected {
if !docker.isConnected {
log.Debug().Msg("Docker not connected, returning empty labels")
return config.App{}, nil
}
containers, err := docker.GetContainers()
containers, err := docker.getContainers()
if err != nil {
return config.App{}, err
}
for _, ctr := range containers {
inspect, err := docker.InspectContainer(ctr.ID)
inspect, err := docker.inspectContainer(ctr.ID)
if err != nil {
return config.App{}, err
}