mirror of
https://github.com/lukaszraczylo/gohoarder.git
synced 2026-07-22 06:20:09 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
113564b497 | ||
|
|
b78e6e79e3 | ||
|
|
3b26752e3c | ||
|
|
ec3312d836 | ||
|
|
f1913087e4 | ||
|
|
eae3e33e7e | ||
|
|
2e8baad645 | ||
|
|
e6f127cf9e | ||
|
|
a1e16c989b | ||
|
|
955250650e | ||
|
|
85c76150f5 | ||
|
|
1baf0993de | ||
|
|
434a15076e | ||
|
|
4e7350363d | ||
|
|
38edd735b6 | ||
|
|
c207aa72e9 | ||
|
|
bf0925a4fc | ||
|
|
bc854aa183 | ||
|
|
c4bb2f6e3a | ||
|
|
8848656193 | ||
|
|
3ecff61114 | ||
|
|
f86943b884 | ||
|
|
448bb70ac8 | ||
|
|
d63ae21133 | ||
|
|
f71414bbb1 | ||
|
|
bdcacfc8db | ||
|
|
a36080c6de | ||
|
|
0e6510636d | ||
|
|
276d406fb6 | ||
|
|
fb79d82072 | ||
|
|
bbcbeeaf88 | ||
|
|
9f4d8fc777 | ||
|
|
1f781f6620 | ||
|
|
8a9d786b1a | ||
|
|
e1a02a6d69 | ||
|
|
5c8565367c | ||
|
|
9db929ed8b | ||
|
|
0a2c5f6c2c | ||
|
|
ae632a3dd7 | ||
|
|
c212077478 | ||
|
|
af5e08a864 | ||
|
|
adc7388ee1 | ||
|
|
116ba9b006 | ||
|
|
03860dcb49 | ||
|
|
a1ec05b210 | ||
|
|
dc1d507a20 | ||
|
|
72f284f987 | ||
|
|
ef11972274 | ||
|
|
96f9f4a36c |
@@ -20,10 +20,15 @@ permissions:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
release:
|
release:
|
||||||
uses: lukaszraczylo/shared-actions/.github/workflows/go-release.yaml@main
|
uses: lukaszraczylo/shared-actions/.github/workflows/go-release-cgo.yaml@main
|
||||||
with:
|
with:
|
||||||
go-version: "1.25"
|
go-version: "1.25"
|
||||||
docker-enabled: true
|
# Enable frontend build
|
||||||
|
node-enabled: true
|
||||||
|
node-version: "20"
|
||||||
|
node-build-script: "cd frontend && npm install -g pnpm && pnpm install --frozen-lockfile && pnpm run build"
|
||||||
|
node-output-path: "frontend/dist"
|
||||||
|
node-cache-dependency-path: "frontend/pnpm-lock.yaml"
|
||||||
secrets: inherit
|
secrets: inherit
|
||||||
|
|
||||||
benchmark:
|
benchmark:
|
||||||
|
|||||||
+148
-83
@@ -7,19 +7,91 @@ project_name: gohoarder
|
|||||||
before:
|
before:
|
||||||
hooks:
|
hooks:
|
||||||
- go mod tidy
|
- go mod tidy
|
||||||
# Generate semantic version if not provided via git tag
|
# Download and setup Zig for cross-compilation (Linux only)
|
||||||
# This script can be used by CI/CD to inject custom versions
|
- bash -c 'if [ "$(uname -s)" = "Linux" ]; then wget https://ziglang.org/download/0.15.2/zig-x86_64-linux-0.15.2.tar.xz -O /tmp/zig.tar.xz && ls -lh /tmp/zig.tar.xz && tar -xvf /tmp/zig.tar.xz -C /tmp && echo "/tmp/zig-x86_64-linux-0.15.2" >> $GITHUB_PATH; fi'
|
||||||
# Usage: export GORELEASER_CURRENT_TAG=$(./script/generate-version.sh)
|
|
||||||
# - ./script/generate-version.sh
|
|
||||||
|
|
||||||
# Build configuration
|
# Build configuration
|
||||||
# Note: Binaries are built inside Docker containers (multi-stage builds)
|
# All binaries built using Zig for consistent cross-compilation
|
||||||
# to avoid CGO cross-compilation issues. No standalone builds here.
|
# Zig handles CGO cross-compilation without platform-specific toolchains
|
||||||
|
# Binaries are injected into Docker images (no Docker compilation)
|
||||||
builds:
|
builds:
|
||||||
- id: gohoarder
|
- id: gohoarder
|
||||||
skip: true
|
main: ./cmd/gohoarder
|
||||||
|
binary: gohoarder
|
||||||
|
env:
|
||||||
|
- CGO_ENABLED=1
|
||||||
|
tags:
|
||||||
|
- fts5
|
||||||
|
flags:
|
||||||
|
- -trimpath
|
||||||
|
ldflags:
|
||||||
|
- -s -w
|
||||||
|
- -X github.com/lukaszraczylo/gohoarder/internal/version.Version={{.Version}}
|
||||||
|
- -X github.com/lukaszraczylo/gohoarder/internal/version.GitCommit={{.ShortCommit}}
|
||||||
|
- -X github.com/lukaszraczylo/gohoarder/internal/version.BuildTime={{.Date}}
|
||||||
|
goos:
|
||||||
|
- linux
|
||||||
|
- darwin
|
||||||
|
goarch:
|
||||||
|
- amd64
|
||||||
|
- arm64
|
||||||
|
ignore:
|
||||||
|
- goos: darwin
|
||||||
|
goarch: amd64
|
||||||
|
overrides:
|
||||||
|
# Use Zig only for Linux cross-compilation
|
||||||
|
- goos: linux
|
||||||
|
goarch: amd64
|
||||||
|
env:
|
||||||
|
- CGO_ENABLED=1
|
||||||
|
- CC=/tmp/zig-x86_64-linux-0.15.2/zig cc -target x86_64-linux-musl
|
||||||
|
- CXX=/tmp/zig-x86_64-linux-0.15.2/zig c++ -target x86_64-linux-musl
|
||||||
|
- goos: linux
|
||||||
|
goarch: arm64
|
||||||
|
env:
|
||||||
|
- CGO_ENABLED=1
|
||||||
|
- CC=/tmp/zig-x86_64-linux-0.15.2/zig cc -target aarch64-linux-musl
|
||||||
|
- CXX=/tmp/zig-x86_64-linux-0.15.2/zig c++ -target aarch64-linux-musl
|
||||||
|
# darwin/arm64 builds natively on macOS runner (no Zig)
|
||||||
|
|
||||||
- id: migrate
|
- id: migrate
|
||||||
skip: true
|
main: ./cmd/migrate
|
||||||
|
binary: migrate
|
||||||
|
env:
|
||||||
|
- CGO_ENABLED=1
|
||||||
|
tags:
|
||||||
|
- fts5
|
||||||
|
flags:
|
||||||
|
- -trimpath
|
||||||
|
ldflags:
|
||||||
|
- -s -w
|
||||||
|
- -X main.Version={{.Version}}
|
||||||
|
- -X main.GitCommit={{.ShortCommit}}
|
||||||
|
- -X main.BuildTime={{.Date}}
|
||||||
|
goos:
|
||||||
|
- linux
|
||||||
|
- darwin
|
||||||
|
goarch:
|
||||||
|
- amd64
|
||||||
|
- arm64
|
||||||
|
ignore:
|
||||||
|
- goos: darwin
|
||||||
|
goarch: amd64
|
||||||
|
overrides:
|
||||||
|
# Use Zig only for Linux cross-compilation
|
||||||
|
- goos: linux
|
||||||
|
goarch: amd64
|
||||||
|
env:
|
||||||
|
- CGO_ENABLED=1
|
||||||
|
- CC=/tmp/zig-x86_64-linux-0.15.2/zig cc -target x86_64-linux-musl
|
||||||
|
- CXX=/tmp/zig-x86_64-linux-0.15.2/zig c++ -target x86_64-linux-musl
|
||||||
|
- goos: linux
|
||||||
|
goarch: arm64
|
||||||
|
env:
|
||||||
|
- CGO_ENABLED=1
|
||||||
|
- CC=/tmp/zig-x86_64-linux-0.15.2/zig cc -target aarch64-linux-musl
|
||||||
|
- CXX=/tmp/zig-x86_64-linux-0.15.2/zig c++ -target aarch64-linux-musl
|
||||||
|
# darwin/arm64 builds natively on macOS runner (no Zig)
|
||||||
|
|
||||||
# Archives for releases
|
# Archives for releases
|
||||||
archives:
|
archives:
|
||||||
@@ -76,121 +148,114 @@ release:
|
|||||||
prerelease: auto
|
prerelease: auto
|
||||||
|
|
||||||
# Docker images (v2 - modern syntax)
|
# Docker images (v2 - modern syntax)
|
||||||
|
# Uses PRE-BUILT binaries from native builds (no Docker compilation - much faster!)
|
||||||
|
# GoReleaser injects the platform-specific binary into each Docker image automatically
|
||||||
|
# This avoids slow QEMU emulation for cross-architecture builds
|
||||||
dockers_v2:
|
dockers_v2:
|
||||||
# 1. Application Engine - Main GoHoarder server
|
# 1. Application Engine - Main GoHoarder server
|
||||||
|
# Uses pre-built binary from 'gohoarder' build (no Docker compilation)
|
||||||
|
# linux/arm64 binary is cross-compiled using Zig (fast, no QEMU!)
|
||||||
- id: gohoarder-server
|
- id: gohoarder-server
|
||||||
use: buildx
|
ids:
|
||||||
|
- gohoarder
|
||||||
images:
|
images:
|
||||||
- ghcr.io/lukaszraczylo/gohoarder-server
|
- ghcr.io/lukaszraczylo/gohoarder-server
|
||||||
build_flag_templates:
|
|
||||||
- "--platform=linux/amd64,linux/arm64"
|
|
||||||
- "--build-arg=VERSION={{ .Version }}"
|
|
||||||
- "--build-arg=GIT_COMMIT={{ .ShortCommit }}"
|
|
||||||
- "--build-arg=BUILD_TIME={{ .Date }}"
|
|
||||||
tags:
|
tags:
|
||||||
- "{{ .Version }}"
|
- "{{ .Version }}"
|
||||||
- latest
|
- latest
|
||||||
|
platforms:
|
||||||
|
- linux/amd64
|
||||||
|
- linux/arm64
|
||||||
dockerfile: Dockerfile.server
|
dockerfile: Dockerfile.server
|
||||||
labels:
|
flags:
|
||||||
org.opencontainers.image.title: GoHoarder Server
|
- "--pull"
|
||||||
org.opencontainers.image.description: Universal package cache proxy server
|
- "--label=org.opencontainers.image.title=GoHoarder Server"
|
||||||
org.opencontainers.image.url: https://github.com/lukaszraczylo/gohoarder
|
- "--label=org.opencontainers.image.description=Universal package cache proxy server"
|
||||||
org.opencontainers.image.source: https://github.com/lukaszraczylo/gohoarder
|
- "--label=org.opencontainers.image.url=https://github.com/lukaszraczylo/gohoarder"
|
||||||
org.opencontainers.image.version: "{{ .Version }}"
|
- "--label=org.opencontainers.image.source=https://github.com/lukaszraczylo/gohoarder"
|
||||||
org.opencontainers.image.created: "{{ .Date }}"
|
- "--label=org.opencontainers.image.version={{ .Version }}"
|
||||||
org.opencontainers.image.revision: "{{ .FullCommit }}"
|
- "--label=org.opencontainers.image.created={{ .Date }}"
|
||||||
|
- "--label=org.opencontainers.image.revision={{ .FullCommit }}"
|
||||||
extra_files:
|
extra_files:
|
||||||
- config.yaml.example
|
- config.yaml.example
|
||||||
|
|
||||||
# 2. Website - Frontend Dashboard
|
# 2. Website - Frontend Dashboard
|
||||||
|
# Note: Frontend is pre-built on CI runner and injected via frontend/dist
|
||||||
- id: gohoarder-frontend
|
- id: gohoarder-frontend
|
||||||
use: buildx
|
|
||||||
images:
|
images:
|
||||||
- ghcr.io/lukaszraczylo/gohoarder-frontend
|
- ghcr.io/lukaszraczylo/gohoarder-frontend
|
||||||
build_flag_templates:
|
|
||||||
- "--platform=linux/amd64,linux/arm64"
|
|
||||||
tags:
|
tags:
|
||||||
- "{{ .Version }}"
|
- "{{ .Version }}"
|
||||||
- latest
|
- latest
|
||||||
|
platforms:
|
||||||
|
- linux/amd64
|
||||||
|
- linux/arm64
|
||||||
dockerfile: Dockerfile.frontend
|
dockerfile: Dockerfile.frontend
|
||||||
labels:
|
flags:
|
||||||
org.opencontainers.image.title: GoHoarder Frontend
|
- "--pull"
|
||||||
org.opencontainers.image.description: GoHoarder web dashboard
|
- "--label=org.opencontainers.image.title=GoHoarder Frontend"
|
||||||
org.opencontainers.image.url: https://github.com/lukaszraczylo/gohoarder
|
- "--label=org.opencontainers.image.description=GoHoarder web dashboard"
|
||||||
org.opencontainers.image.source: https://github.com/lukaszraczylo/gohoarder
|
- "--label=org.opencontainers.image.url=https://github.com/lukaszraczylo/gohoarder"
|
||||||
org.opencontainers.image.version: "{{ .Version }}"
|
- "--label=org.opencontainers.image.source=https://github.com/lukaszraczylo/gohoarder"
|
||||||
org.opencontainers.image.created: "{{ .Date }}"
|
- "--label=org.opencontainers.image.version={{ .Version }}"
|
||||||
org.opencontainers.image.revision: "{{ .FullCommit }}"
|
- "--label=org.opencontainers.image.created={{ .Date }}"
|
||||||
|
- "--label=org.opencontainers.image.revision={{ .FullCommit }}"
|
||||||
extra_files:
|
extra_files:
|
||||||
- frontend
|
- frontend/dist
|
||||||
|
|
||||||
# 3. Scanning Engine - Background scanner worker
|
# 3. Scanning Engine - Background scanner worker
|
||||||
|
# Uses pre-built binary from 'gohoarder' build (no Docker compilation)
|
||||||
|
# linux/arm64 binary is cross-compiled using Zig (fast, no QEMU!)
|
||||||
- id: gohoarder-scanner
|
- id: gohoarder-scanner
|
||||||
use: buildx
|
ids:
|
||||||
|
- gohoarder
|
||||||
images:
|
images:
|
||||||
- ghcr.io/lukaszraczylo/gohoarder-scanner
|
- ghcr.io/lukaszraczylo/gohoarder-scanner
|
||||||
build_flag_templates:
|
|
||||||
- "--platform=linux/amd64,linux/arm64"
|
|
||||||
- "--build-arg=VERSION={{ .Version }}"
|
|
||||||
- "--build-arg=GIT_COMMIT={{ .ShortCommit }}"
|
|
||||||
- "--build-arg=BUILD_TIME={{ .Date }}"
|
|
||||||
tags:
|
tags:
|
||||||
- "{{ .Version }}"
|
- "{{ .Version }}"
|
||||||
- latest
|
- latest
|
||||||
|
platforms:
|
||||||
|
- linux/amd64
|
||||||
|
- linux/arm64
|
||||||
dockerfile: Dockerfile.scanner
|
dockerfile: Dockerfile.scanner
|
||||||
labels:
|
flags:
|
||||||
org.opencontainers.image.title: GoHoarder Scanner
|
- "--pull"
|
||||||
org.opencontainers.image.description: GoHoarder vulnerability scanning engine
|
- "--label=org.opencontainers.image.title=GoHoarder Scanner"
|
||||||
org.opencontainers.image.url: https://github.com/lukaszraczylo/gohoarder
|
- "--label=org.opencontainers.image.description=GoHoarder vulnerability scanning engine"
|
||||||
org.opencontainers.image.source: https://github.com/lukaszraczylo/gohoarder
|
- "--label=org.opencontainers.image.url=https://github.com/lukaszraczylo/gohoarder"
|
||||||
org.opencontainers.image.version: "{{ .Version }}"
|
- "--label=org.opencontainers.image.source=https://github.com/lukaszraczylo/gohoarder"
|
||||||
org.opencontainers.image.created: "{{ .Date }}"
|
- "--label=org.opencontainers.image.version={{ .Version }}"
|
||||||
org.opencontainers.image.revision: "{{ .FullCommit }}"
|
- "--label=org.opencontainers.image.created={{ .Date }}"
|
||||||
|
- "--label=org.opencontainers.image.revision={{ .FullCommit }}"
|
||||||
extra_files:
|
extra_files:
|
||||||
- config.yaml.example
|
- config.yaml.example
|
||||||
|
|
||||||
# 4. Gateway - Nginx reverse proxy for unified deployment
|
# 4. Migration Engine - Database migration tool
|
||||||
- id: gohoarder-gateway
|
# Uses pre-built binary from 'migrate' build (no Docker compilation)
|
||||||
use: buildx
|
# linux/arm64 binary is cross-compiled using Zig (fast, no QEMU!)
|
||||||
images:
|
|
||||||
- ghcr.io/lukaszraczylo/gohoarder-gateway
|
|
||||||
build_flag_templates:
|
|
||||||
- "--platform=linux/amd64,linux/arm64"
|
|
||||||
tags:
|
|
||||||
- "{{ .Version }}"
|
|
||||||
- latest
|
|
||||||
dockerfile: Dockerfile.gateway
|
|
||||||
labels:
|
|
||||||
org.opencontainers.image.title: GoHoarder Gateway
|
|
||||||
org.opencontainers.image.description: Nginx reverse proxy for unified GoHoarder deployment
|
|
||||||
org.opencontainers.image.url: https://github.com/lukaszraczylo/gohoarder
|
|
||||||
org.opencontainers.image.source: https://github.com/lukaszraczylo/gohoarder
|
|
||||||
org.opencontainers.image.version: "{{ .Version }}"
|
|
||||||
org.opencontainers.image.created: "{{ .Date }}"
|
|
||||||
org.opencontainers.image.revision: "{{ .FullCommit }}"
|
|
||||||
|
|
||||||
# 5. Migration Engine - Database migration tool
|
|
||||||
- id: gohoarder-migrate
|
- id: gohoarder-migrate
|
||||||
use: buildx
|
ids:
|
||||||
|
- migrate
|
||||||
images:
|
images:
|
||||||
- ghcr.io/lukaszraczylo/gohoarder-migrate
|
- ghcr.io/lukaszraczylo/gohoarder-migrate
|
||||||
build_flag_templates:
|
|
||||||
- "--platform=linux/amd64,linux/arm64"
|
|
||||||
- "--build-arg=VERSION={{ .Version }}"
|
|
||||||
- "--build-arg=GIT_COMMIT={{ .ShortCommit }}"
|
|
||||||
- "--build-arg=BUILD_TIME={{ .Date }}"
|
|
||||||
tags:
|
tags:
|
||||||
- "{{ .Version }}"
|
- "{{ .Version }}"
|
||||||
- latest
|
- latest
|
||||||
|
platforms:
|
||||||
|
- linux/amd64
|
||||||
|
- linux/arm64
|
||||||
dockerfile: Dockerfile.migrate
|
dockerfile: Dockerfile.migrate
|
||||||
labels:
|
flags:
|
||||||
org.opencontainers.image.title: GoHoarder Migrate
|
- "--pull"
|
||||||
org.opencontainers.image.description: Database migration tool for GoHoarder V2 schema
|
- "--label=org.opencontainers.image.title=GoHoarder Migrate"
|
||||||
org.opencontainers.image.url: https://github.com/lukaszraczylo/gohoarder
|
- "--label=org.opencontainers.image.description=Database migration tool for GoHoarder V2 schema"
|
||||||
org.opencontainers.image.source: https://github.com/lukaszraczylo/gohoarder
|
- "--label=org.opencontainers.image.url=https://github.com/lukaszraczylo/gohoarder"
|
||||||
org.opencontainers.image.version: "{{ .Version }}"
|
- "--label=org.opencontainers.image.source=https://github.com/lukaszraczylo/gohoarder"
|
||||||
org.opencontainers.image.created: "{{ .Date }}"
|
- "--label=org.opencontainers.image.version={{ .Version }}"
|
||||||
org.opencontainers.image.revision: "{{ .FullCommit }}"
|
- "--label=org.opencontainers.image.created={{ .Date }}"
|
||||||
|
- "--label=org.opencontainers.image.revision={{ .FullCommit }}"
|
||||||
|
extra_files:
|
||||||
|
- migrations
|
||||||
|
|
||||||
# Artifact signing with cosign
|
# Artifact signing with cosign
|
||||||
signs:
|
signs:
|
||||||
|
|||||||
+127
-32
@@ -1,28 +1,15 @@
|
|||||||
# Website - Frontend Dashboard
|
# Website - Frontend Dashboard with integrated reverse proxy
|
||||||
# Build stage
|
# Combines frontend serving and backend proxy (previously separate gateway)
|
||||||
FROM node:20-alpine AS builder
|
# EXPECTS: Pre-built frontend files in frontend/dist/ directory
|
||||||
|
|
||||||
WORKDIR /build
|
|
||||||
|
|
||||||
# Copy frontend source
|
|
||||||
COPY frontend/package.json frontend/pnpm-lock.yaml ./
|
|
||||||
COPY frontend/ ./
|
|
||||||
|
|
||||||
# Install pnpm and dependencies
|
|
||||||
RUN npm install -g pnpm && \
|
|
||||||
pnpm install --frozen-lockfile
|
|
||||||
|
|
||||||
# Build the frontend
|
|
||||||
RUN pnpm run build
|
|
||||||
|
|
||||||
# Production stage
|
|
||||||
FROM nginx:alpine
|
FROM nginx:alpine
|
||||||
|
|
||||||
# Install envsubst for runtime configuration
|
# Install envsubst for runtime configuration
|
||||||
RUN apk add --no-cache gettext
|
RUN apk add --no-cache gettext
|
||||||
|
|
||||||
# Copy built frontend
|
# Copy pre-built frontend files
|
||||||
COPY --from=builder /build/dist /usr/share/nginx/html
|
# These are built on the CI runner and injected via extra_files
|
||||||
|
COPY frontend/dist /usr/share/nginx/html
|
||||||
|
|
||||||
# Create runtime config injection script
|
# Create runtime config injection script
|
||||||
RUN cat > /docker-entrypoint.d/40-inject-config.sh <<'EOF'
|
RUN cat > /docker-entrypoint.d/40-inject-config.sh <<'EOF'
|
||||||
@@ -48,21 +35,121 @@ EOF
|
|||||||
|
|
||||||
RUN chmod +x /docker-entrypoint.d/40-inject-config.sh
|
RUN chmod +x /docker-entrypoint.d/40-inject-config.sh
|
||||||
|
|
||||||
# Copy nginx configuration
|
# Create nginx templates directory and configuration template with backend proxy support
|
||||||
RUN cat > /etc/nginx/conf.d/default.conf <<'EOF'
|
RUN mkdir -p /etc/nginx/templates && \
|
||||||
|
cat > /etc/nginx/templates/default.conf.template <<'EOF'
|
||||||
|
# Upstream backend server
|
||||||
|
upstream backend {
|
||||||
|
server ${BACKEND_HOST}:${BACKEND_PORT};
|
||||||
|
keepalive 32;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Cache configuration
|
||||||
|
proxy_cache_path /var/cache/nginx/static levels=1:2 keys_zone=static_cache:10m max_size=100m inactive=60m use_temp_path=off;
|
||||||
|
|
||||||
server {
|
server {
|
||||||
listen 80;
|
listen 80;
|
||||||
server_name _;
|
server_name ${SERVER_NAME};
|
||||||
root /usr/share/nginx/html;
|
root /usr/share/nginx/html;
|
||||||
index index.html;
|
index index.html;
|
||||||
|
|
||||||
|
# Security headers
|
||||||
|
add_header X-Frame-Options "SAMEORIGIN" always;
|
||||||
|
add_header X-Content-Type-Options "nosniff" always;
|
||||||
|
add_header X-XSS-Protection "1; mode=block" always;
|
||||||
|
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
|
||||||
|
|
||||||
|
# Client body size for package uploads
|
||||||
|
client_max_body_size 500M;
|
||||||
|
client_body_timeout 300s;
|
||||||
|
|
||||||
|
# Logging
|
||||||
|
access_log /var/log/nginx/access.log combined;
|
||||||
|
error_log /var/log/nginx/error.log warn;
|
||||||
|
|
||||||
# Compression
|
# Compression
|
||||||
gzip on;
|
gzip on;
|
||||||
gzip_types text/plain text/css application/json application/javascript text/xml application/xml text/javascript;
|
gzip_types text/plain text/css application/json application/javascript text/xml application/xml text/javascript;
|
||||||
|
|
||||||
# SPA routing
|
# API endpoints - proxy to backend
|
||||||
location / {
|
location /api/ {
|
||||||
try_files $uri $uri/ /index.html;
|
# Proxy settings
|
||||||
|
proxy_pass http://backend;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
|
||||||
|
# Headers
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
proxy_set_header X-Forwarded-Host $host;
|
||||||
|
proxy_set_header X-Forwarded-Port $server_port;
|
||||||
|
proxy_set_header Connection "";
|
||||||
|
|
||||||
|
# Timeouts for long-running operations
|
||||||
|
proxy_connect_timeout 60s;
|
||||||
|
proxy_send_timeout 300s;
|
||||||
|
proxy_read_timeout 300s;
|
||||||
|
|
||||||
|
# Buffer settings
|
||||||
|
proxy_buffering on;
|
||||||
|
proxy_buffer_size 4k;
|
||||||
|
proxy_buffers 8 4k;
|
||||||
|
proxy_busy_buffers_size 8k;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Health check endpoint
|
||||||
|
location /health {
|
||||||
|
proxy_pass http://backend/health;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Connection "";
|
||||||
|
access_log off;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Metrics endpoint
|
||||||
|
location /metrics {
|
||||||
|
proxy_pass http://backend/metrics;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Connection "";
|
||||||
|
}
|
||||||
|
|
||||||
|
# Package download endpoints
|
||||||
|
location ~ ^/(npm|pypi|go)/ {
|
||||||
|
proxy_pass http://backend;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
proxy_set_header Connection "";
|
||||||
|
|
||||||
|
# Extended timeouts for package downloads
|
||||||
|
proxy_connect_timeout 60s;
|
||||||
|
proxy_send_timeout 600s;
|
||||||
|
proxy_read_timeout 600s;
|
||||||
|
|
||||||
|
# Large buffer for package downloads
|
||||||
|
proxy_buffering on;
|
||||||
|
proxy_buffer_size 128k;
|
||||||
|
proxy_buffers 4 256k;
|
||||||
|
proxy_busy_buffers_size 256k;
|
||||||
|
}
|
||||||
|
|
||||||
|
# WebSocket support
|
||||||
|
location /ws/ {
|
||||||
|
proxy_pass http://backend/ws/;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
|
proxy_set_header Connection "upgrade";
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
|
||||||
|
# WebSocket timeouts
|
||||||
|
proxy_connect_timeout 7d;
|
||||||
|
proxy_send_timeout 7d;
|
||||||
|
proxy_read_timeout 7d;
|
||||||
}
|
}
|
||||||
|
|
||||||
# Runtime configuration endpoint
|
# Runtime configuration endpoint
|
||||||
@@ -72,19 +159,23 @@ server {
|
|||||||
add_header Expires "0";
|
add_header Expires "0";
|
||||||
}
|
}
|
||||||
|
|
||||||
# Security headers
|
|
||||||
add_header X-Frame-Options "SAMEORIGIN" always;
|
|
||||||
add_header X-Content-Type-Options "nosniff" always;
|
|
||||||
add_header X-XSS-Protection "1; mode=block" always;
|
|
||||||
|
|
||||||
# Cache static assets
|
# Cache static assets
|
||||||
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
|
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
|
||||||
expires 1y;
|
expires 1y;
|
||||||
add_header Cache-Control "public, immutable";
|
add_header Cache-Control "public, immutable";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Frontend SPA - serve index.html for all other routes
|
||||||
|
location / {
|
||||||
|
try_files $uri $uri/ /index.html;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
|
# Create cache directory
|
||||||
|
RUN mkdir -p /var/cache/nginx/static && \
|
||||||
|
chown -R nginx:nginx /var/cache/nginx
|
||||||
|
|
||||||
# Expose port
|
# Expose port
|
||||||
EXPOSE 80
|
EXPOSE 80
|
||||||
|
|
||||||
@@ -95,6 +186,10 @@ HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
|
|||||||
# Environment variables with defaults
|
# Environment variables with defaults
|
||||||
ENV API_BASE_URL=/api \
|
ENV API_BASE_URL=/api \
|
||||||
APP_VERSION=unknown \
|
APP_VERSION=unknown \
|
||||||
APP_NAME=GoHoarder
|
APP_NAME=GoHoarder \
|
||||||
|
BACKEND_HOST=gohoarder-server \
|
||||||
|
BACKEND_PORT=8080 \
|
||||||
|
SERVER_NAME=_
|
||||||
|
|
||||||
CMD ["nginx", "-g", "daemon off;"]
|
# Use nginx template substitution and start nginx
|
||||||
|
CMD ["/bin/sh", "-c", "envsubst '$$BACKEND_HOST $$BACKEND_PORT $$SERVER_NAME' < /etc/nginx/templates/default.conf.template > /etc/nginx/conf.d/default.conf && nginx -g 'daemon off;'"]
|
||||||
|
|||||||
@@ -1,197 +0,0 @@
|
|||||||
# Gateway - Nginx reverse proxy for unified deployment
|
|
||||||
# Routes traffic between frontend and backend under single vhost
|
|
||||||
FROM nginx:alpine
|
|
||||||
|
|
||||||
# Install envsubst for runtime configuration
|
|
||||||
RUN apk add --no-cache gettext
|
|
||||||
|
|
||||||
# Copy nginx configuration template
|
|
||||||
COPY <<'EOF' /etc/nginx/templates/default.conf.template
|
|
||||||
# Upstream servers
|
|
||||||
upstream backend {
|
|
||||||
server ${BACKEND_HOST}:${BACKEND_PORT};
|
|
||||||
keepalive 32;
|
|
||||||
}
|
|
||||||
|
|
||||||
upstream frontend {
|
|
||||||
server ${FRONTEND_HOST}:${FRONTEND_PORT};
|
|
||||||
keepalive 32;
|
|
||||||
}
|
|
||||||
|
|
||||||
# Rate limiting zones
|
|
||||||
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;
|
|
||||||
limit_req_zone $binary_remote_addr zone=download_limit:10m rate=5r/s;
|
|
||||||
|
|
||||||
# Cache configuration
|
|
||||||
proxy_cache_path /var/cache/nginx/static levels=1:2 keys_zone=static_cache:10m max_size=100m inactive=60m use_temp_path=off;
|
|
||||||
|
|
||||||
server {
|
|
||||||
listen 80;
|
|
||||||
server_name ${SERVER_NAME};
|
|
||||||
|
|
||||||
# Security headers
|
|
||||||
add_header X-Frame-Options "SAMEORIGIN" always;
|
|
||||||
add_header X-Content-Type-Options "nosniff" always;
|
|
||||||
add_header X-XSS-Protection "1; mode=block" always;
|
|
||||||
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
|
|
||||||
|
|
||||||
# Client body size for package uploads
|
|
||||||
client_max_body_size 500M;
|
|
||||||
client_body_timeout 300s;
|
|
||||||
|
|
||||||
# Logging
|
|
||||||
access_log /var/log/nginx/access.log combined;
|
|
||||||
error_log /var/log/nginx/error.log warn;
|
|
||||||
|
|
||||||
# API endpoints - proxy to backend
|
|
||||||
location /api/ {
|
|
||||||
# Rate limiting
|
|
||||||
limit_req zone=api_limit burst=20 nodelay;
|
|
||||||
|
|
||||||
# Proxy settings
|
|
||||||
proxy_pass http://backend/;
|
|
||||||
proxy_http_version 1.1;
|
|
||||||
|
|
||||||
# Headers
|
|
||||||
proxy_set_header Host $host;
|
|
||||||
proxy_set_header X-Real-IP $remote_addr;
|
|
||||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
||||||
proxy_set_header X-Forwarded-Proto $scheme;
|
|
||||||
proxy_set_header X-Forwarded-Host $host;
|
|
||||||
proxy_set_header X-Forwarded-Port $server_port;
|
|
||||||
|
|
||||||
# Connection reuse
|
|
||||||
proxy_set_header Connection "";
|
|
||||||
|
|
||||||
# Timeouts for long-running operations
|
|
||||||
proxy_connect_timeout 60s;
|
|
||||||
proxy_send_timeout 300s;
|
|
||||||
proxy_read_timeout 300s;
|
|
||||||
|
|
||||||
# Buffer settings
|
|
||||||
proxy_buffering on;
|
|
||||||
proxy_buffer_size 4k;
|
|
||||||
proxy_buffers 8 4k;
|
|
||||||
proxy_busy_buffers_size 8k;
|
|
||||||
}
|
|
||||||
|
|
||||||
# Health check endpoint
|
|
||||||
location /health {
|
|
||||||
proxy_pass http://backend/health;
|
|
||||||
proxy_http_version 1.1;
|
|
||||||
proxy_set_header Connection "";
|
|
||||||
access_log off;
|
|
||||||
}
|
|
||||||
|
|
||||||
# Metrics endpoint (optional - may want to restrict access)
|
|
||||||
location /metrics {
|
|
||||||
# Uncomment to restrict to internal networks
|
|
||||||
# allow 10.0.0.0/8;
|
|
||||||
# allow 172.16.0.0/12;
|
|
||||||
# allow 192.168.0.0/16;
|
|
||||||
# deny all;
|
|
||||||
|
|
||||||
proxy_pass http://backend/metrics;
|
|
||||||
proxy_http_version 1.1;
|
|
||||||
proxy_set_header Connection "";
|
|
||||||
}
|
|
||||||
|
|
||||||
# Package download endpoints with rate limiting
|
|
||||||
location ~ ^/(npm|pypi|go)/ {
|
|
||||||
limit_req zone=download_limit burst=10 nodelay;
|
|
||||||
|
|
||||||
proxy_pass http://backend;
|
|
||||||
proxy_http_version 1.1;
|
|
||||||
proxy_set_header Host $host;
|
|
||||||
proxy_set_header X-Real-IP $remote_addr;
|
|
||||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
||||||
proxy_set_header X-Forwarded-Proto $scheme;
|
|
||||||
proxy_set_header Connection "";
|
|
||||||
|
|
||||||
# Extended timeouts for package downloads
|
|
||||||
proxy_connect_timeout 60s;
|
|
||||||
proxy_send_timeout 600s;
|
|
||||||
proxy_read_timeout 600s;
|
|
||||||
|
|
||||||
# Large buffer for package downloads
|
|
||||||
proxy_buffering on;
|
|
||||||
proxy_buffer_size 128k;
|
|
||||||
proxy_buffers 4 256k;
|
|
||||||
proxy_busy_buffers_size 256k;
|
|
||||||
}
|
|
||||||
|
|
||||||
# Frontend - serve SPA
|
|
||||||
location / {
|
|
||||||
proxy_pass http://frontend;
|
|
||||||
proxy_http_version 1.1;
|
|
||||||
proxy_set_header Host $host;
|
|
||||||
proxy_set_header X-Real-IP $remote_addr;
|
|
||||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
||||||
proxy_set_header X-Forwarded-Proto $scheme;
|
|
||||||
proxy_set_header Connection "";
|
|
||||||
|
|
||||||
# Cache static assets
|
|
||||||
proxy_cache static_cache;
|
|
||||||
proxy_cache_valid 200 1h;
|
|
||||||
proxy_cache_bypass $http_cache_control;
|
|
||||||
add_header X-Cache-Status $upstream_cache_status;
|
|
||||||
}
|
|
||||||
|
|
||||||
# WebSocket support (if needed for future features)
|
|
||||||
location /ws/ {
|
|
||||||
proxy_pass http://backend/ws/;
|
|
||||||
proxy_http_version 1.1;
|
|
||||||
proxy_set_header Upgrade $http_upgrade;
|
|
||||||
proxy_set_header Connection "upgrade";
|
|
||||||
proxy_set_header Host $host;
|
|
||||||
proxy_set_header X-Real-IP $remote_addr;
|
|
||||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
||||||
proxy_set_header X-Forwarded-Proto $scheme;
|
|
||||||
|
|
||||||
# WebSocket timeouts
|
|
||||||
proxy_connect_timeout 7d;
|
|
||||||
proxy_send_timeout 7d;
|
|
||||||
proxy_read_timeout 7d;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# HTTPS server (uncomment and configure SSL certificates)
|
|
||||||
# server {
|
|
||||||
# listen 443 ssl http2;
|
|
||||||
# server_name ${SERVER_NAME};
|
|
||||||
#
|
|
||||||
# ssl_certificate /etc/nginx/ssl/cert.pem;
|
|
||||||
# ssl_certificate_key /etc/nginx/ssl/key.pem;
|
|
||||||
#
|
|
||||||
# # SSL configuration
|
|
||||||
# ssl_protocols TLSv1.2 TLSv1.3;
|
|
||||||
# ssl_ciphers HIGH:!aNULL:!MD5;
|
|
||||||
# ssl_prefer_server_ciphers on;
|
|
||||||
# ssl_session_cache shared:SSL:10m;
|
|
||||||
# ssl_session_timeout 10m;
|
|
||||||
#
|
|
||||||
# # Include all location blocks from above
|
|
||||||
# # ... (copy from HTTP server block)
|
|
||||||
# }
|
|
||||||
EOF
|
|
||||||
|
|
||||||
# Create cache directory
|
|
||||||
RUN mkdir -p /var/cache/nginx/static && \
|
|
||||||
chown -R nginx:nginx /var/cache/nginx
|
|
||||||
|
|
||||||
# Expose port
|
|
||||||
EXPOSE 80 443
|
|
||||||
|
|
||||||
# Health check
|
|
||||||
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
|
|
||||||
CMD wget --quiet --tries=1 --spider http://localhost/health || exit 1
|
|
||||||
|
|
||||||
# Environment variables with defaults
|
|
||||||
ENV BACKEND_HOST=gohoarder-server \
|
|
||||||
BACKEND_PORT=8080 \
|
|
||||||
FRONTEND_HOST=gohoarder-frontend \
|
|
||||||
FRONTEND_PORT=80 \
|
|
||||||
SERVER_NAME=_
|
|
||||||
|
|
||||||
# Use nginx with template substitution
|
|
||||||
CMD ["/bin/sh", "-c", "envsubst '$$BACKEND_HOST $$BACKEND_PORT $$FRONTEND_HOST $$FRONTEND_PORT $$SERVER_NAME' < /etc/nginx/templates/default.conf.template > /etc/nginx/conf.d/default.conf && nginx -g 'daemon off;'"]
|
|
||||||
+10
-39
@@ -1,44 +1,10 @@
|
|||||||
# Migration Engine - Database Migration Tool
|
# Migration Engine - Database Migration Tool
|
||||||
# Multi-stage build to compile with CGO support
|
# This Dockerfile expects a PRE-BUILT binary from GoReleaser (no compilation)
|
||||||
ARG TARGETOS=linux
|
# GoReleaser injects the platform-specific binary automatically
|
||||||
ARG TARGETARCH=amd64
|
|
||||||
|
|
||||||
# Build stage
|
FROM --platform=$TARGETPLATFORM alpine:latest
|
||||||
FROM --platform=$TARGETOS/$TARGETARCH golang:1.23-alpine AS builder
|
|
||||||
|
|
||||||
# Install build dependencies for CGO
|
|
||||||
RUN apk add --no-cache \
|
|
||||||
gcc \
|
|
||||||
g++ \
|
|
||||||
musl-dev \
|
|
||||||
sqlite-dev \
|
|
||||||
git
|
|
||||||
|
|
||||||
WORKDIR /build
|
|
||||||
|
|
||||||
# Copy go mod files
|
|
||||||
COPY go.mod go.sum ./
|
|
||||||
RUN go mod download
|
|
||||||
|
|
||||||
# Copy source code
|
|
||||||
COPY . .
|
|
||||||
|
|
||||||
# Build with CGO enabled
|
|
||||||
ARG TARGETOS
|
|
||||||
ARG TARGETARCH
|
ARG TARGETARCH
|
||||||
ARG VERSION=dev
|
|
||||||
ARG GIT_COMMIT=unknown
|
|
||||||
ARG BUILD_TIME=unknown
|
|
||||||
|
|
||||||
RUN CGO_ENABLED=1 GOOS=$TARGETOS GOARCH=$TARGETARCH \
|
|
||||||
go build -ldflags="-s -w \
|
|
||||||
-X main.Version=${VERSION} \
|
|
||||||
-X main.GitCommit=${GIT_COMMIT} \
|
|
||||||
-X main.BuildTime=${BUILD_TIME}" \
|
|
||||||
-o migrate ./cmd/migrate
|
|
||||||
|
|
||||||
# Runtime stage
|
|
||||||
FROM alpine:latest
|
|
||||||
|
|
||||||
# Install runtime dependencies (including CGO/SQLite dependencies)
|
# Install runtime dependencies (including CGO/SQLite dependencies)
|
||||||
RUN apk add --no-cache \
|
RUN apk add --no-cache \
|
||||||
@@ -54,10 +20,15 @@ RUN apk add --no-cache \
|
|||||||
RUN addgroup -g 1000 gohoarder && \
|
RUN addgroup -g 1000 gohoarder && \
|
||||||
adduser -D -u 1000 -G gohoarder gohoarder
|
adduser -D -u 1000 -G gohoarder gohoarder
|
||||||
|
|
||||||
# Copy binary from builder
|
# Copy pre-built binary from GoReleaser
|
||||||
COPY --from=builder /build/migrate /usr/local/bin/migrate
|
# GoReleaser will automatically inject the correct binary for the target platform
|
||||||
|
# In split/merge mode, binaries are in linux/${TARGETARCH}/ subdirectories
|
||||||
|
COPY linux/${TARGETARCH}/migrate /usr/local/bin/migrate
|
||||||
RUN chmod +x /usr/local/bin/migrate
|
RUN chmod +x /usr/local/bin/migrate
|
||||||
|
|
||||||
|
# Copy migration SQL files
|
||||||
|
COPY migrations /migrations
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
USER gohoarder
|
USER gohoarder
|
||||||
|
|
||||||
|
|||||||
+18
-39
@@ -1,44 +1,10 @@
|
|||||||
# Scanning Engine - Background Scanner Worker
|
# Scanning Engine - Background Scanner Worker
|
||||||
# Multi-stage build to compile with CGO support
|
# This Dockerfile expects a PRE-BUILT binary from GoReleaser (no compilation)
|
||||||
ARG TARGETOS=linux
|
# GoReleaser injects the platform-specific binary automatically
|
||||||
ARG TARGETARCH=amd64
|
|
||||||
|
|
||||||
# Build stage
|
FROM --platform=$TARGETPLATFORM alpine:latest
|
||||||
FROM --platform=$TARGETOS/$TARGETARCH golang:1.23-alpine AS builder
|
|
||||||
|
|
||||||
# Install build dependencies for CGO
|
|
||||||
RUN apk add --no-cache \
|
|
||||||
gcc \
|
|
||||||
g++ \
|
|
||||||
musl-dev \
|
|
||||||
sqlite-dev \
|
|
||||||
git
|
|
||||||
|
|
||||||
WORKDIR /build
|
|
||||||
|
|
||||||
# Copy go mod files
|
|
||||||
COPY go.mod go.sum ./
|
|
||||||
RUN go mod download
|
|
||||||
|
|
||||||
# Copy source code
|
|
||||||
COPY . .
|
|
||||||
|
|
||||||
# Build with CGO enabled
|
|
||||||
ARG TARGETOS
|
|
||||||
ARG TARGETARCH
|
ARG TARGETARCH
|
||||||
ARG VERSION=dev
|
|
||||||
ARG GIT_COMMIT=unknown
|
|
||||||
ARG BUILD_TIME=unknown
|
|
||||||
|
|
||||||
RUN CGO_ENABLED=1 GOOS=$TARGETOS GOARCH=$TARGETARCH \
|
|
||||||
go build -ldflags="-s -w \
|
|
||||||
-X github.com/lukaszraczylo/gohoarder/internal/version.Version=${VERSION} \
|
|
||||||
-X github.com/lukaszraczylo/gohoarder/internal/version.GitCommit=${GIT_COMMIT} \
|
|
||||||
-X github.com/lukaszraczylo/gohoarder/internal/version.BuildTime=${BUILD_TIME}" \
|
|
||||||
-o gohoarder ./cmd/gohoarder
|
|
||||||
|
|
||||||
# Runtime stage
|
|
||||||
FROM alpine:latest
|
|
||||||
|
|
||||||
# Install scanning tools and runtime dependencies (including CGO/SQLite dependencies)
|
# Install scanning tools and runtime dependencies (including CGO/SQLite dependencies)
|
||||||
RUN apk add --no-cache \
|
RUN apk add --no-cache \
|
||||||
@@ -50,6 +16,10 @@ RUN apk add --no-cache \
|
|||||||
bash \
|
bash \
|
||||||
sqlite-libs \
|
sqlite-libs \
|
||||||
musl \
|
musl \
|
||||||
|
python3 \
|
||||||
|
py3-pip \
|
||||||
|
npm \
|
||||||
|
go \
|
||||||
&& update-ca-certificates
|
&& update-ca-certificates
|
||||||
|
|
||||||
# Install Trivy for container scanning
|
# Install Trivy for container scanning
|
||||||
@@ -58,6 +28,13 @@ RUN curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/
|
|||||||
# Install Grype for vulnerability scanning
|
# Install Grype for vulnerability scanning
|
||||||
RUN curl -sSfL https://raw.githubusercontent.com/anchore/grype/main/install.sh | sh -s -- -b /usr/local/bin
|
RUN curl -sSfL https://raw.githubusercontent.com/anchore/grype/main/install.sh | sh -s -- -b /usr/local/bin
|
||||||
|
|
||||||
|
# Install govulncheck for Go vulnerability scanning
|
||||||
|
RUN go install golang.org/x/vuln/cmd/govulncheck@latest && \
|
||||||
|
mv /root/go/bin/govulncheck /usr/local/bin/
|
||||||
|
|
||||||
|
# Install pip-audit for Python package vulnerability scanning
|
||||||
|
RUN pip3 install --no-cache-dir pip-audit --break-system-packages
|
||||||
|
|
||||||
# Create non-root user
|
# Create non-root user
|
||||||
RUN addgroup -g 1000 scanner && \
|
RUN addgroup -g 1000 scanner && \
|
||||||
adduser -D -u 1000 -G scanner scanner
|
adduser -D -u 1000 -G scanner scanner
|
||||||
@@ -75,8 +52,10 @@ RUN mkdir -p /var/cache/gohoarder \
|
|||||||
/var/lib/gohoarder \
|
/var/lib/gohoarder \
|
||||||
/var/lib/trivy
|
/var/lib/trivy
|
||||||
|
|
||||||
# Copy binary from builder
|
# Copy pre-built binary from GoReleaser
|
||||||
COPY --from=builder /build/gohoarder /usr/local/bin/gohoarder
|
# GoReleaser will automatically inject the correct binary for the target platform
|
||||||
|
# In split/merge mode, binaries are in linux/${TARGETARCH}/ subdirectories
|
||||||
|
COPY linux/${TARGETARCH}/gohoarder /usr/local/bin/gohoarder
|
||||||
RUN chmod +x /usr/local/bin/gohoarder
|
RUN chmod +x /usr/local/bin/gohoarder
|
||||||
|
|
||||||
# Copy example config
|
# Copy example config
|
||||||
|
|||||||
+9
-41
@@ -1,46 +1,12 @@
|
|||||||
# Application Engine - GoHoarder Server
|
# Application Engine - Main GoHoarder Server
|
||||||
# Multi-stage build to compile with CGO support
|
# This Dockerfile expects a PRE-BUILT binary from GoReleaser (no compilation)
|
||||||
ARG TARGETOS=linux
|
# GoReleaser injects the platform-specific binary automatically
|
||||||
ARG TARGETARCH=amd64
|
|
||||||
|
|
||||||
# Build stage
|
FROM --platform=$TARGETPLATFORM alpine:latest
|
||||||
FROM --platform=$TARGETOS/$TARGETARCH golang:1.23-alpine AS builder
|
|
||||||
|
|
||||||
# Install build dependencies for CGO
|
|
||||||
RUN apk add --no-cache \
|
|
||||||
gcc \
|
|
||||||
g++ \
|
|
||||||
musl-dev \
|
|
||||||
sqlite-dev \
|
|
||||||
git
|
|
||||||
|
|
||||||
WORKDIR /build
|
|
||||||
|
|
||||||
# Copy go mod files
|
|
||||||
COPY go.mod go.sum ./
|
|
||||||
RUN go mod download
|
|
||||||
|
|
||||||
# Copy source code
|
|
||||||
COPY . .
|
|
||||||
|
|
||||||
# Build with CGO enabled
|
|
||||||
ARG TARGETOS
|
|
||||||
ARG TARGETARCH
|
ARG TARGETARCH
|
||||||
ARG VERSION=dev
|
|
||||||
ARG GIT_COMMIT=unknown
|
|
||||||
ARG BUILD_TIME=unknown
|
|
||||||
|
|
||||||
RUN CGO_ENABLED=1 GOOS=$TARGETOS GOARCH=$TARGETARCH \
|
# Install runtime dependencies (CGO/SQLite requires these)
|
||||||
go build -ldflags="-s -w \
|
|
||||||
-X github.com/lukaszraczylo/gohoarder/internal/version.Version=${VERSION} \
|
|
||||||
-X github.com/lukaszraczylo/gohoarder/internal/version.GitCommit=${GIT_COMMIT} \
|
|
||||||
-X github.com/lukaszraczylo/gohoarder/internal/version.BuildTime=${BUILD_TIME}" \
|
|
||||||
-o gohoarder ./cmd/gohoarder
|
|
||||||
|
|
||||||
# Runtime stage
|
|
||||||
FROM alpine:latest
|
|
||||||
|
|
||||||
# Install runtime dependencies (including CGO/SQLite dependencies)
|
|
||||||
RUN apk add --no-cache \
|
RUN apk add --no-cache \
|
||||||
ca-certificates \
|
ca-certificates \
|
||||||
tzdata \
|
tzdata \
|
||||||
@@ -62,8 +28,10 @@ RUN mkdir -p /var/cache/gohoarder \
|
|||||||
chmod -R 750 /var/cache/gohoarder \
|
chmod -R 750 /var/cache/gohoarder \
|
||||||
/var/lib/gohoarder
|
/var/lib/gohoarder
|
||||||
|
|
||||||
# Copy binary from builder
|
# Copy pre-built binary from GoReleaser
|
||||||
COPY --from=builder /build/gohoarder /usr/local/bin/gohoarder
|
# GoReleaser will automatically inject the correct binary for the target platform
|
||||||
|
# In split/merge mode, binaries are in linux/${TARGETARCH}/ subdirectories
|
||||||
|
COPY linux/${TARGETARCH}/gohoarder /usr/local/bin/gohoarder
|
||||||
RUN chmod +x /usr/local/bin/gohoarder
|
RUN chmod +x /usr/local/bin/gohoarder
|
||||||
|
|
||||||
# Copy example config
|
# Copy example config
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
# GoHoarder - Kubernetes Deployment (All-in-One)
|
# GoHoarder - Kubernetes Deployment (All-in-One)
|
||||||
# This manifest deploys all GoHoarder services under a single ingress
|
# This manifest deploys all GoHoarder services under a single ingress
|
||||||
|
# The frontend includes integrated nginx reverse proxy to the backend
|
||||||
#
|
#
|
||||||
# Usage:
|
# Usage:
|
||||||
# kubectl create namespace gohoarder
|
# kubectl create namespace gohoarder
|
||||||
@@ -222,6 +223,13 @@ spec:
|
|||||||
value: "1.0.0"
|
value: "1.0.0"
|
||||||
- name: APP_NAME
|
- name: APP_NAME
|
||||||
value: GoHoarder
|
value: GoHoarder
|
||||||
|
# Backend proxy configuration (frontend includes nginx reverse proxy)
|
||||||
|
- name: BACKEND_HOST
|
||||||
|
value: gohoarder-server
|
||||||
|
- name: BACKEND_PORT
|
||||||
|
value: "8080"
|
||||||
|
- name: SERVER_NAME
|
||||||
|
value: hoarder.i.raczylo.com
|
||||||
livenessProbe:
|
livenessProbe:
|
||||||
httpGet:
|
httpGet:
|
||||||
path: /
|
path: /
|
||||||
@@ -325,88 +333,6 @@ spec:
|
|||||||
configMap:
|
configMap:
|
||||||
name: gohoarder-config
|
name: gohoarder-config
|
||||||
|
|
||||||
---
|
|
||||||
# Deployment - Gateway (Nginx Reverse Proxy)
|
|
||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: gohoarder-gateway
|
|
||||||
namespace: gohoarder
|
|
||||||
labels:
|
|
||||||
app.kubernetes.io/name: gohoarder
|
|
||||||
app.kubernetes.io/component: gateway
|
|
||||||
spec:
|
|
||||||
replicas: 2
|
|
||||||
selector:
|
|
||||||
matchLabels:
|
|
||||||
app.kubernetes.io/name: gohoarder
|
|
||||||
app.kubernetes.io/component: gateway
|
|
||||||
template:
|
|
||||||
metadata:
|
|
||||||
labels:
|
|
||||||
app.kubernetes.io/name: gohoarder
|
|
||||||
app.kubernetes.io/component: gateway
|
|
||||||
spec:
|
|
||||||
containers:
|
|
||||||
- name: gateway
|
|
||||||
image: ghcr.io/lukaszraczylo/gohoarder-gateway:latest
|
|
||||||
imagePullPolicy: Always
|
|
||||||
ports:
|
|
||||||
- name: http
|
|
||||||
containerPort: 80
|
|
||||||
protocol: TCP
|
|
||||||
env:
|
|
||||||
- name: BACKEND_HOST
|
|
||||||
value: gohoarder-server
|
|
||||||
- name: BACKEND_PORT
|
|
||||||
value: "8080"
|
|
||||||
- name: FRONTEND_HOST
|
|
||||||
value: gohoarder-frontend
|
|
||||||
- name: FRONTEND_PORT
|
|
||||||
value: "80"
|
|
||||||
- name: SERVER_NAME
|
|
||||||
value: hoarder.i.raczylo.com
|
|
||||||
livenessProbe:
|
|
||||||
httpGet:
|
|
||||||
path: /health
|
|
||||||
port: 80
|
|
||||||
initialDelaySeconds: 5
|
|
||||||
periodSeconds: 30
|
|
||||||
readinessProbe:
|
|
||||||
httpGet:
|
|
||||||
path: /health
|
|
||||||
port: 80
|
|
||||||
initialDelaySeconds: 5
|
|
||||||
periodSeconds: 10
|
|
||||||
resources:
|
|
||||||
requests:
|
|
||||||
cpu: 100m
|
|
||||||
memory: 128Mi
|
|
||||||
limits:
|
|
||||||
cpu: 500m
|
|
||||||
memory: 256Mi
|
|
||||||
|
|
||||||
---
|
|
||||||
# Service - Gateway
|
|
||||||
apiVersion: v1
|
|
||||||
kind: Service
|
|
||||||
metadata:
|
|
||||||
name: gohoarder-gateway
|
|
||||||
namespace: gohoarder
|
|
||||||
labels:
|
|
||||||
app.kubernetes.io/name: gohoarder
|
|
||||||
app.kubernetes.io/component: gateway
|
|
||||||
spec:
|
|
||||||
type: ClusterIP
|
|
||||||
ports:
|
|
||||||
- name: http
|
|
||||||
port: 80
|
|
||||||
targetPort: http
|
|
||||||
protocol: TCP
|
|
||||||
selector:
|
|
||||||
app.kubernetes.io/name: gohoarder
|
|
||||||
app.kubernetes.io/component: gateway
|
|
||||||
|
|
||||||
---
|
---
|
||||||
# Ingress - Expose via domain
|
# Ingress - Expose via domain
|
||||||
apiVersion: networking.k8s.io/v1
|
apiVersion: networking.k8s.io/v1
|
||||||
@@ -436,7 +362,7 @@ spec:
|
|||||||
pathType: Prefix
|
pathType: Prefix
|
||||||
backend:
|
backend:
|
||||||
service:
|
service:
|
||||||
name: gohoarder-gateway
|
name: gohoarder-frontend
|
||||||
port:
|
port:
|
||||||
number: 80
|
number: 80
|
||||||
# Uncomment for HTTPS/TLS
|
# Uncomment for HTTPS/TLS
|
||||||
@@ -477,20 +403,20 @@ spec:
|
|||||||
averageUtilization: 80
|
averageUtilization: 80
|
||||||
|
|
||||||
---
|
---
|
||||||
# HorizontalPodAutoscaler - Gateway
|
# HorizontalPodAutoscaler - Frontend
|
||||||
apiVersion: autoscaling/v2
|
apiVersion: autoscaling/v2
|
||||||
kind: HorizontalPodAutoscaler
|
kind: HorizontalPodAutoscaler
|
||||||
metadata:
|
metadata:
|
||||||
name: gohoarder-gateway
|
name: gohoarder-frontend
|
||||||
namespace: gohoarder
|
namespace: gohoarder
|
||||||
labels:
|
labels:
|
||||||
app.kubernetes.io/name: gohoarder
|
app.kubernetes.io/name: gohoarder
|
||||||
app.kubernetes.io/component: gateway
|
app.kubernetes.io/component: frontend
|
||||||
spec:
|
spec:
|
||||||
scaleTargetRef:
|
scaleTargetRef:
|
||||||
apiVersion: apps/v1
|
apiVersion: apps/v1
|
||||||
kind: Deployment
|
kind: Deployment
|
||||||
name: gohoarder-gateway
|
name: gohoarder-frontend
|
||||||
minReplicas: 2
|
minReplicas: 2
|
||||||
maxReplicas: 10
|
maxReplicas: 10
|
||||||
metrics:
|
metrics:
|
||||||
|
|||||||
+14
-38
@@ -2,7 +2,7 @@ version: '3.8'
|
|||||||
|
|
||||||
# GoHoarder - Unified Deployment Example
|
# GoHoarder - Unified Deployment Example
|
||||||
# This docker-compose file demonstrates deploying all GoHoarder services
|
# This docker-compose file demonstrates deploying all GoHoarder services
|
||||||
# under a single domain using the gateway reverse proxy
|
# The frontend includes integrated reverse proxy to the backend
|
||||||
|
|
||||||
services:
|
services:
|
||||||
# Backend - Main application server
|
# Backend - Main application server
|
||||||
@@ -39,7 +39,7 @@ services:
|
|||||||
retries: 3
|
retries: 3
|
||||||
start_period: 5s
|
start_period: 5s
|
||||||
|
|
||||||
# Frontend - Web dashboard
|
# Frontend - Web dashboard with integrated reverse proxy
|
||||||
gohoarder-frontend:
|
gohoarder-frontend:
|
||||||
image: ghcr.io/lukaszraczylo/gohoarder-frontend:latest
|
image: ghcr.io/lukaszraczylo/gohoarder-frontend:latest
|
||||||
container_name: gohoarder-frontend
|
container_name: gohoarder-frontend
|
||||||
@@ -49,8 +49,19 @@ services:
|
|||||||
- API_BASE_URL=/api
|
- API_BASE_URL=/api
|
||||||
- APP_VERSION=1.0.0
|
- APP_VERSION=1.0.0
|
||||||
- APP_NAME=GoHoarder
|
- APP_NAME=GoHoarder
|
||||||
|
# Backend proxy configuration (frontend includes nginx reverse proxy)
|
||||||
|
- BACKEND_HOST=gohoarder-server
|
||||||
|
- BACKEND_PORT=8080
|
||||||
|
- SERVER_NAME=hoarder.i.raczylo.com
|
||||||
|
ports:
|
||||||
|
# Map to host port 80 (HTTP)
|
||||||
|
- "80:80"
|
||||||
|
# Map to host port 443 (HTTPS) - uncomment if using SSL
|
||||||
|
# - "443:443"
|
||||||
networks:
|
networks:
|
||||||
- gohoarder-internal
|
- gohoarder-internal
|
||||||
|
depends_on:
|
||||||
|
- gohoarder-server
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost/"]
|
test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost/"]
|
||||||
interval: 30s
|
interval: 30s
|
||||||
@@ -82,41 +93,6 @@ services:
|
|||||||
# profiles:
|
# profiles:
|
||||||
# - scanner
|
# - scanner
|
||||||
|
|
||||||
# Gateway - Nginx reverse proxy
|
|
||||||
gohoarder-gateway:
|
|
||||||
image: ghcr.io/lukaszraczylo/gohoarder-gateway:latest
|
|
||||||
container_name: gohoarder-gateway
|
|
||||||
restart: unless-stopped
|
|
||||||
environment:
|
|
||||||
# Backend service connection
|
|
||||||
- BACKEND_HOST=gohoarder-server
|
|
||||||
- BACKEND_PORT=8080
|
|
||||||
# Frontend service connection
|
|
||||||
- FRONTEND_HOST=gohoarder-frontend
|
|
||||||
- FRONTEND_PORT=80
|
|
||||||
# Server configuration
|
|
||||||
- SERVER_NAME=hoarder.i.raczylo.com
|
|
||||||
ports:
|
|
||||||
# Map to host port 80 (HTTP)
|
|
||||||
- "80:80"
|
|
||||||
# Map to host port 443 (HTTPS) - uncomment if using SSL
|
|
||||||
# - "443:443"
|
|
||||||
networks:
|
|
||||||
- gohoarder-internal
|
|
||||||
depends_on:
|
|
||||||
- gohoarder-server
|
|
||||||
- gohoarder-frontend
|
|
||||||
# Uncomment if using custom SSL certificates
|
|
||||||
# volumes:
|
|
||||||
# - ./ssl/cert.pem:/etc/nginx/ssl/cert.pem:ro
|
|
||||||
# - ./ssl/key.pem:/etc/nginx/ssl/key.pem:ro
|
|
||||||
healthcheck:
|
|
||||||
test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost/health"]
|
|
||||||
interval: 30s
|
|
||||||
timeout: 10s
|
|
||||||
retries: 3
|
|
||||||
start_period: 5s
|
|
||||||
|
|
||||||
networks:
|
networks:
|
||||||
gohoarder-internal:
|
gohoarder-internal:
|
||||||
driver: bridge
|
driver: bridge
|
||||||
@@ -144,7 +120,7 @@ volumes:
|
|||||||
# - Metrics: http://localhost/metrics
|
# - Metrics: http://localhost/metrics
|
||||||
#
|
#
|
||||||
# For production:
|
# For production:
|
||||||
# - Enable HTTPS in the gateway container
|
# - Enable HTTPS in the frontend container (add SSL certificates to nginx)
|
||||||
# - Set up proper SSL certificates
|
# - Set up proper SSL certificates
|
||||||
# - Configure firewall rules
|
# - Configure firewall rules
|
||||||
# - Set appropriate resource limits
|
# - Set appropriate resource limits
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"hash": "eb707743",
|
||||||
|
"configHash": "e17a1322",
|
||||||
|
"lockfileHash": "c2e83396",
|
||||||
|
"browserHash": "ab796ace",
|
||||||
|
"optimized": {},
|
||||||
|
"chunks": {}
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"type": "module"
|
||||||
|
}
|
||||||
@@ -3,17 +3,17 @@ module github.com/lukaszraczylo/gohoarder
|
|||||||
go 1.25.5
|
go 1.25.5
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/aws/aws-sdk-go-v2 v1.41.0
|
github.com/aws/aws-sdk-go-v2 v1.41.1
|
||||||
github.com/aws/aws-sdk-go-v2/config v1.32.6
|
github.com/aws/aws-sdk-go-v2/config v1.32.7
|
||||||
github.com/aws/aws-sdk-go-v2/credentials v1.19.6
|
github.com/aws/aws-sdk-go-v2/credentials v1.19.7
|
||||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.95.0
|
github.com/aws/aws-sdk-go-v2/service/s3 v1.96.0
|
||||||
github.com/go-gormigrate/gormigrate/v2 v2.1.5
|
github.com/go-gormigrate/gormigrate/v2 v2.1.5
|
||||||
github.com/go-sql-driver/mysql v1.9.3
|
github.com/go-sql-driver/mysql v1.9.3
|
||||||
github.com/goccy/go-json v0.10.5
|
github.com/goccy/go-json v0.10.5
|
||||||
github.com/gofiber/fiber/v2 v2.52.10
|
github.com/gofiber/fiber/v2 v2.52.11
|
||||||
github.com/gorilla/websocket v1.5.3
|
github.com/gorilla/websocket v1.5.3
|
||||||
github.com/hirochachacha/go-smb2 v1.1.0
|
github.com/hirochachacha/go-smb2 v1.1.0
|
||||||
github.com/lib/pq v1.10.9
|
github.com/lib/pq v1.11.1
|
||||||
github.com/prometheus/client_golang v1.23.2
|
github.com/prometheus/client_golang v1.23.2
|
||||||
github.com/rs/zerolog v1.34.0
|
github.com/rs/zerolog v1.34.0
|
||||||
github.com/spf13/cobra v1.10.2
|
github.com/spf13/cobra v1.10.2
|
||||||
@@ -22,7 +22,7 @@ require (
|
|||||||
github.com/testcontainers/testcontainers-go v0.40.0
|
github.com/testcontainers/testcontainers-go v0.40.0
|
||||||
github.com/testcontainers/testcontainers-go/modules/mysql v0.40.0
|
github.com/testcontainers/testcontainers-go/modules/mysql v0.40.0
|
||||||
github.com/testcontainers/testcontainers-go/modules/postgres v0.40.0
|
github.com/testcontainers/testcontainers-go/modules/postgres v0.40.0
|
||||||
golang.org/x/crypto v0.46.0
|
golang.org/x/crypto v0.47.0
|
||||||
golang.org/x/sync v0.19.0
|
golang.org/x/sync v0.19.0
|
||||||
golang.org/x/time v0.14.0
|
golang.org/x/time v0.14.0
|
||||||
gorm.io/driver/mysql v1.6.0
|
gorm.io/driver/mysql v1.6.0
|
||||||
@@ -38,25 +38,25 @@ require (
|
|||||||
github.com/Microsoft/go-winio v0.6.2 // indirect
|
github.com/Microsoft/go-winio v0.6.2 // indirect
|
||||||
github.com/andybalholm/brotli v1.2.0 // indirect
|
github.com/andybalholm/brotli v1.2.0 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.4 // indirect
|
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.4 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.16 // indirect
|
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.17 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.16 // indirect
|
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.17 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.16 // indirect
|
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.17 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4 // indirect
|
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.16 // indirect
|
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.17 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.4 // indirect
|
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.4 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.7 // indirect
|
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.8 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.16 // indirect
|
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.17 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.16 // indirect
|
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.17 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/service/signin v1.0.4 // indirect
|
github.com/aws/aws-sdk-go-v2/service/signin v1.0.5 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/service/sso v1.30.8 // indirect
|
github.com/aws/aws-sdk-go-v2/service/sso v1.30.9 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.12 // indirect
|
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.13 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/service/sts v1.41.5 // indirect
|
github.com/aws/aws-sdk-go-v2/service/sts v1.41.6 // indirect
|
||||||
github.com/aws/smithy-go v1.24.0 // indirect
|
github.com/aws/smithy-go v1.24.0 // indirect
|
||||||
github.com/beorn7/perks v1.0.1 // indirect
|
github.com/beorn7/perks v1.0.1 // indirect
|
||||||
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
|
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
|
||||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||||
github.com/clipperhouse/stringish v0.1.1 // indirect
|
github.com/clipperhouse/stringish v0.1.1 // indirect
|
||||||
github.com/clipperhouse/uax29/v2 v2.3.0 // indirect
|
github.com/clipperhouse/uax29/v2 v2.4.0 // indirect
|
||||||
github.com/containerd/errdefs v1.0.0 // indirect
|
github.com/containerd/errdefs v1.0.0 // indirect
|
||||||
github.com/containerd/errdefs/pkg v0.3.0 // indirect
|
github.com/containerd/errdefs/pkg v0.3.0 // indirect
|
||||||
github.com/containerd/log v0.1.0 // indirect
|
github.com/containerd/log v0.1.0 // indirect
|
||||||
@@ -74,7 +74,7 @@ require (
|
|||||||
github.com/go-logr/logr v1.4.3 // indirect
|
github.com/go-logr/logr v1.4.3 // indirect
|
||||||
github.com/go-logr/stdr v1.2.2 // indirect
|
github.com/go-logr/stdr v1.2.2 // indirect
|
||||||
github.com/go-ole/go-ole v1.3.0 // indirect
|
github.com/go-ole/go-ole v1.3.0 // indirect
|
||||||
github.com/go-viper/mapstructure/v2 v2.4.0 // indirect
|
github.com/go-viper/mapstructure/v2 v2.5.0 // indirect
|
||||||
github.com/google/uuid v1.6.0 // indirect
|
github.com/google/uuid v1.6.0 // indirect
|
||||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.4 // indirect
|
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.4 // indirect
|
||||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||||
@@ -84,7 +84,7 @@ require (
|
|||||||
github.com/jackc/puddle/v2 v2.2.2 // indirect
|
github.com/jackc/puddle/v2 v2.2.2 // indirect
|
||||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||||
github.com/jinzhu/now v1.1.5 // indirect
|
github.com/jinzhu/now v1.1.5 // indirect
|
||||||
github.com/klauspost/compress v1.18.2 // indirect
|
github.com/klauspost/compress v1.18.3 // indirect
|
||||||
github.com/lufia/plan9stats v0.0.0-20251013123823-9fd1530e3ec3 // indirect
|
github.com/lufia/plan9stats v0.0.0-20251013123823-9fd1530e3ec3 // indirect
|
||||||
github.com/magiconair/properties v1.8.10 // indirect
|
github.com/magiconair/properties v1.8.10 // indirect
|
||||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||||
@@ -107,7 +107,7 @@ require (
|
|||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
|
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
|
||||||
github.com/prometheus/client_model v0.6.2 // indirect
|
github.com/prometheus/client_model v0.6.2 // indirect
|
||||||
github.com/prometheus/common v0.67.4 // indirect
|
github.com/prometheus/common v0.67.5 // indirect
|
||||||
github.com/prometheus/procfs v0.19.2 // indirect
|
github.com/prometheus/procfs v0.19.2 // indirect
|
||||||
github.com/sagikazarmark/locafero v0.12.0 // indirect
|
github.com/sagikazarmark/locafero v0.12.0 // indirect
|
||||||
github.com/shirou/gopsutil/v4 v4.25.12 // indirect
|
github.com/shirou/gopsutil/v4 v4.25.12 // indirect
|
||||||
@@ -120,7 +120,7 @@ require (
|
|||||||
github.com/tklauser/go-sysconf v0.3.16 // indirect
|
github.com/tklauser/go-sysconf v0.3.16 // indirect
|
||||||
github.com/tklauser/numcpus v0.11.0 // indirect
|
github.com/tklauser/numcpus v0.11.0 // indirect
|
||||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||||
github.com/valyala/fasthttp v1.68.0 // indirect
|
github.com/valyala/fasthttp v1.69.0 // indirect
|
||||||
github.com/yusufpapurcu/wmi v1.2.4 // indirect
|
github.com/yusufpapurcu/wmi v1.2.4 // indirect
|
||||||
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
|
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
|
||||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.64.0 // indirect
|
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.64.0 // indirect
|
||||||
@@ -129,8 +129,8 @@ require (
|
|||||||
go.opentelemetry.io/otel/trace v1.39.0 // indirect
|
go.opentelemetry.io/otel/trace v1.39.0 // indirect
|
||||||
go.yaml.in/yaml/v2 v2.4.3 // indirect
|
go.yaml.in/yaml/v2 v2.4.3 // indirect
|
||||||
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
||||||
golang.org/x/sys v0.39.0 // indirect
|
golang.org/x/sys v0.40.0 // indirect
|
||||||
golang.org/x/text v0.32.0 // indirect
|
golang.org/x/text v0.33.0 // indirect
|
||||||
google.golang.org/grpc v1.78.0 // indirect
|
google.golang.org/grpc v1.78.0 // indirect
|
||||||
google.golang.org/protobuf v1.36.11 // indirect
|
google.golang.org/protobuf v1.36.11 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
|
|||||||
@@ -10,42 +10,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/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
|
||||||
github.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ=
|
github.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ=
|
||||||
github.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY=
|
github.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY=
|
||||||
github.com/aws/aws-sdk-go-v2 v1.41.0 h1:tNvqh1s+v0vFYdA1xq0aOJH+Y5cRyZ5upu6roPgPKd4=
|
github.com/aws/aws-sdk-go-v2 v1.41.1 h1:ABlyEARCDLN034NhxlRUSZr4l71mh+T5KAeGh6cerhU=
|
||||||
github.com/aws/aws-sdk-go-v2 v1.41.0/go.mod h1:MayyLB8y+buD9hZqkCW3kX1AKq07Y5pXxtgB+rRFhz0=
|
github.com/aws/aws-sdk-go-v2 v1.41.1/go.mod h1:MayyLB8y+buD9hZqkCW3kX1AKq07Y5pXxtgB+rRFhz0=
|
||||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.4 h1:489krEF9xIGkOaaX3CE/Be2uWjiXrkCH6gUX+bZA/BU=
|
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.4 h1:489krEF9xIGkOaaX3CE/Be2uWjiXrkCH6gUX+bZA/BU=
|
||||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.4/go.mod h1:IOAPF6oT9KCsceNTvvYMNHy0+kMF8akOjeDvPENWxp4=
|
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.4/go.mod h1:IOAPF6oT9KCsceNTvvYMNHy0+kMF8akOjeDvPENWxp4=
|
||||||
github.com/aws/aws-sdk-go-v2/config v1.32.6 h1:hFLBGUKjmLAekvi1evLi5hVvFQtSo3GYwi+Bx4lpJf8=
|
github.com/aws/aws-sdk-go-v2/config v1.32.7 h1:vxUyWGUwmkQ2g19n7JY/9YL8MfAIl7bTesIUykECXmY=
|
||||||
github.com/aws/aws-sdk-go-v2/config v1.32.6/go.mod h1:lcUL/gcd8WyjCrMnxez5OXkO3/rwcNmvfno62tnXNcI=
|
github.com/aws/aws-sdk-go-v2/config v1.32.7/go.mod h1:2/Qm5vKUU/r7Y+zUk/Ptt2MDAEKAfUtKc1+3U1Mo3oY=
|
||||||
github.com/aws/aws-sdk-go-v2/credentials v1.19.6 h1:F9vWao2TwjV2MyiyVS+duza0NIRtAslgLUM0vTA1ZaE=
|
github.com/aws/aws-sdk-go-v2/credentials v1.19.7 h1:tHK47VqqtJxOymRrNtUXN5SP/zUTvZKeLx4tH6PGQc8=
|
||||||
github.com/aws/aws-sdk-go-v2/credentials v1.19.6/go.mod h1:SgHzKjEVsdQr6Opor0ihgWtkWdfRAIwxYzSJ8O85VHY=
|
github.com/aws/aws-sdk-go-v2/credentials v1.19.7/go.mod h1:qOZk8sPDrxhf+4Wf4oT2urYJrYt3RejHSzgAquYeppw=
|
||||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.16 h1:80+uETIWS1BqjnN9uJ0dBUaETh+P1XwFy5vwHwK5r9k=
|
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.17 h1:I0GyV8wiYrP8XpA70g1HBcQO1JlQxCMTW9npl5UbDHY=
|
||||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.16/go.mod h1:wOOsYuxYuB/7FlnVtzeBYRcjSRtQpAW0hCP7tIULMwo=
|
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.17/go.mod h1:tyw7BOl5bBe/oqvoIeECFJjMdzXoa/dfVz3QQ5lgHGA=
|
||||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.16 h1:rgGwPzb82iBYSvHMHXc8h9mRoOUBZIGFgKb9qniaZZc=
|
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.17 h1:xOLELNKGp2vsiteLsvLPwxC+mYmO6OZ8PYgiuPJzF8U=
|
||||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.16/go.mod h1:L/UxsGeKpGoIj6DxfhOWHWQ/kGKcd4I1VncE4++IyKA=
|
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.17/go.mod h1:5M5CI3D12dNOtH3/mk6minaRwI2/37ifCURZISxA/IQ=
|
||||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.16 h1:1jtGzuV7c82xnqOVfx2F0xmJcOw5374L7N6juGW6x6U=
|
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.17 h1:WWLqlh79iO48yLkj1v3ISRNiv+3KdQoZ6JWyfcsyQik=
|
||||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.16/go.mod h1:M2E5OQf+XLe+SZGmmpaI2yy+J326aFf6/+54PoxSANc=
|
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.17/go.mod h1:EhG22vHRrvF8oXSTYStZhJc1aUgKtnJe+aOiFEV90cM=
|
||||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4 h1:WKuaxf++XKWlHWu9ECbMlha8WOEGm0OUEZqm4K/Gcfk=
|
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4 h1:WKuaxf++XKWlHWu9ECbMlha8WOEGm0OUEZqm4K/Gcfk=
|
||||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4/go.mod h1:ZWy7j6v1vWGmPReu0iSGvRiise4YI5SkR3OHKTZ6Wuc=
|
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4/go.mod h1:ZWy7j6v1vWGmPReu0iSGvRiise4YI5SkR3OHKTZ6Wuc=
|
||||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.16 h1:CjMzUs78RDDv4ROu3JnJn/Ig1r6ZD7/T2DXLLRpejic=
|
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.17 h1:JqcdRG//czea7Ppjb+g/n4o8i/R50aTBHkA7vu0lK+k=
|
||||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.16/go.mod h1:uVW4OLBqbJXSHJYA9svT9BluSvvwbzLQ2Crf6UPzR3c=
|
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.17/go.mod h1:CO+WeGmIdj/MlPel2KwID9Gt7CNq4M65HUfBW97liM0=
|
||||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.4 h1:0ryTNEdJbzUCEWkVXEXoqlXV72J5keC1GvILMOuD00E=
|
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.4 h1:0ryTNEdJbzUCEWkVXEXoqlXV72J5keC1GvILMOuD00E=
|
||||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.4/go.mod h1:HQ4qwNZh32C3CBeO6iJLQlgtMzqeG17ziAA/3KDJFow=
|
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.4/go.mod h1:HQ4qwNZh32C3CBeO6iJLQlgtMzqeG17ziAA/3KDJFow=
|
||||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.7 h1:DIBqIrJ7hv+e4CmIk2z3pyKT+3B6qVMgRsawHiR3qso=
|
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.8 h1:Z5EiPIzXKewUQK0QTMkutjiaPVeVYXX7KIqhXu/0fXs=
|
||||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.7/go.mod h1:vLm00xmBke75UmpNvOcZQ/Q30ZFjbczeLFqGx5urmGo=
|
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.8/go.mod h1:FsTpJtvC4U1fyDXk7c71XoDv3HlRm8V3NiYLeYLh5YE=
|
||||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.16 h1:oHjJHeUy0ImIV0bsrX0X91GkV5nJAyv1l1CC9lnO0TI=
|
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.17 h1:RuNSMoozM8oXlgLG/n6WLaFGoea7/CddrCfIiSA+xdY=
|
||||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.16/go.mod h1:iRSNGgOYmiYwSCXxXaKb9HfOEj40+oTKn8pTxMlYkRM=
|
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.17/go.mod h1:F2xxQ9TZz5gDWsclCtPQscGpP0VUOc8RqgFM3vDENmU=
|
||||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.16 h1:NSbvS17MlI2lurYgXnCOLvCFX38sBW4eiVER7+kkgsU=
|
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.17 h1:bGeHBsGZx0Dvu/eJC0Lh9adJa3M1xREcndxLNZlve2U=
|
||||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.16/go.mod h1:SwT8Tmqd4sA6G1qaGdzWCJN99bUmPGHfRwwq3G5Qb+A=
|
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.17/go.mod h1:dcW24lbU0CzHusTE8LLHhRLI42ejmINN8Lcr22bwh/g=
|
||||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.95.0 h1:MIWra+MSq53CFaXXAywB2qg9YvVZifkk6vEGl/1Qor0=
|
github.com/aws/aws-sdk-go-v2/service/s3 v1.96.0 h1:oeu8VPlOre74lBA/PMhxa5vewaMIMmILM+RraSyB8KA=
|
||||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.95.0/go.mod h1:79S2BdqCJpScXZA2y+cpZuocWsjGjJINyXnOsf5DTz8=
|
github.com/aws/aws-sdk-go-v2/service/s3 v1.96.0/go.mod h1:5jggDlZ2CLQhwJBiZJb4vfk4f0GxWdEDruWKEJ1xOdo=
|
||||||
github.com/aws/aws-sdk-go-v2/service/signin v1.0.4 h1:HpI7aMmJ+mm1wkSHIA2t5EaFFv5EFYXePW30p1EIrbQ=
|
github.com/aws/aws-sdk-go-v2/service/signin v1.0.5 h1:VrhDvQib/i0lxvr3zqlUwLwJP4fpmpyD9wYG1vfSu+Y=
|
||||||
github.com/aws/aws-sdk-go-v2/service/signin v1.0.4/go.mod h1:C5RdGMYGlfM0gYq/tifqgn4EbyX99V15P2V3R+VHbQU=
|
github.com/aws/aws-sdk-go-v2/service/signin v1.0.5/go.mod h1:k029+U8SY30/3/ras4G/Fnv/b88N4mAfliNn08Dem4M=
|
||||||
github.com/aws/aws-sdk-go-v2/service/sso v1.30.8 h1:aM/Q24rIlS3bRAhTyFurowU8A0SMyGDtEOY/l/s/1Uw=
|
github.com/aws/aws-sdk-go-v2/service/sso v1.30.9 h1:v6EiMvhEYBoHABfbGB4alOYmCIrcgyPPiBE1wZAEbqk=
|
||||||
github.com/aws/aws-sdk-go-v2/service/sso v1.30.8/go.mod h1:+fWt2UHSb4kS7Pu8y+BMBvJF0EWx+4H0hzNwtDNRTrg=
|
github.com/aws/aws-sdk-go-v2/service/sso v1.30.9/go.mod h1:yifAsgBxgJWn3ggx70A3urX2AN49Y5sJTD1UQFlfqBw=
|
||||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.12 h1:AHDr0DaHIAo8c9t1emrzAlVDFp+iMMKnPdYy6XO4MCE=
|
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.13 h1:gd84Omyu9JLriJVCbGApcLzVR3XtmC4ZDPcAI6Ftvds=
|
||||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.12/go.mod h1:GQ73XawFFiWxyWXMHWfhiomvP3tXtdNar/fi8z18sx0=
|
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.13/go.mod h1:sTGThjphYE4Ohw8vJiRStAcu3rbjtXRsdNB0TvZ5wwo=
|
||||||
github.com/aws/aws-sdk-go-v2/service/sts v1.41.5 h1:SciGFVNZ4mHdm7gpD1dgZYnCuVdX1s+lFTg4+4DOy70=
|
github.com/aws/aws-sdk-go-v2/service/sts v1.41.6 h1:5fFjR/ToSOzB2OQ/XqWpZBmNvmP/pJ1jOWYlFDJTjRQ=
|
||||||
github.com/aws/aws-sdk-go-v2/service/sts v1.41.5/go.mod h1:iW40X4QBmUxdP+fZNOpfmkdMZqsovezbAeO+Ubiv2pk=
|
github.com/aws/aws-sdk-go-v2/service/sts v1.41.6/go.mod h1:qgFDZQSD/Kys7nJnVqYlWKnh0SSdMjAi0uSwON4wgYQ=
|
||||||
github.com/aws/smithy-go v1.24.0 h1:LpilSUItNPFr1eY85RYgTIg5eIEPtvFbskaFcmmIUnk=
|
github.com/aws/smithy-go v1.24.0 h1:LpilSUItNPFr1eY85RYgTIg5eIEPtvFbskaFcmmIUnk=
|
||||||
github.com/aws/smithy-go v1.24.0/go.mod h1:LEj2LM3rBRQJxPZTB4KuzZkaZYnZPnvgIhb4pu07mx0=
|
github.com/aws/smithy-go v1.24.0/go.mod h1:LEj2LM3rBRQJxPZTB4KuzZkaZYnZPnvgIhb4pu07mx0=
|
||||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||||
@@ -56,8 +56,8 @@ github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UF
|
|||||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
github.com/clipperhouse/stringish v0.1.1 h1:+NSqMOr3GR6k1FdRhhnXrLfztGzuG+VuFDfatpWHKCs=
|
github.com/clipperhouse/stringish v0.1.1 h1:+NSqMOr3GR6k1FdRhhnXrLfztGzuG+VuFDfatpWHKCs=
|
||||||
github.com/clipperhouse/stringish v0.1.1/go.mod h1:v/WhFtE1q0ovMta2+m+UbpZ+2/HEXNWYXQgCt4hdOzA=
|
github.com/clipperhouse/stringish v0.1.1/go.mod h1:v/WhFtE1q0ovMta2+m+UbpZ+2/HEXNWYXQgCt4hdOzA=
|
||||||
github.com/clipperhouse/uax29/v2 v2.3.0 h1:SNdx9DVUqMoBuBoW3iLOj4FQv3dN5mDtuqwuhIGpJy4=
|
github.com/clipperhouse/uax29/v2 v2.4.0 h1:RXqE/l5EiAbA4u97giimKNlmpvkmz+GrBVTelsoXy9g=
|
||||||
github.com/clipperhouse/uax29/v2 v2.3.0/go.mod h1:Wn1g7MK6OoeDT0vL+Q0SQLDz/KpfsVRgg6W7ihQeh4g=
|
github.com/clipperhouse/uax29/v2 v2.4.0/go.mod h1:Wn1g7MK6OoeDT0vL+Q0SQLDz/KpfsVRgg6W7ihQeh4g=
|
||||||
github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI=
|
github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI=
|
||||||
github.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M=
|
github.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M=
|
||||||
github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151Xdx3ZPPE=
|
github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151Xdx3ZPPE=
|
||||||
@@ -106,13 +106,13 @@ github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
|
|||||||
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
|
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
|
||||||
github.com/go-sql-driver/mysql v1.9.3 h1:U/N249h2WzJ3Ukj8SowVFjdtZKfu9vlLZxjPXV1aweo=
|
github.com/go-sql-driver/mysql v1.9.3 h1:U/N249h2WzJ3Ukj8SowVFjdtZKfu9vlLZxjPXV1aweo=
|
||||||
github.com/go-sql-driver/mysql v1.9.3/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU=
|
github.com/go-sql-driver/mysql v1.9.3/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU=
|
||||||
github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs=
|
github.com/go-viper/mapstructure/v2 v2.5.0 h1:vM5IJoUAy3d7zRSVtIwQgBj7BiWtMPfmPEgAXnvj1Ro=
|
||||||
github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
github.com/go-viper/mapstructure/v2 v2.5.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
||||||
github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
|
github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
|
||||||
github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
|
github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
|
||||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||||
github.com/gofiber/fiber/v2 v2.52.10 h1:jRHROi2BuNti6NYXmZ6gbNSfT3zj/8c0xy94GOU5elY=
|
github.com/gofiber/fiber/v2 v2.52.11 h1:5f4yzKLcBcF8ha1GQTWB+mpblWz3Vz6nSAbTL31HkWs=
|
||||||
github.com/gofiber/fiber/v2 v2.52.10/go.mod h1:YEcBbO/FB+5M1IZNBP9FO3J9281zgPAreiI1oqg8nDw=
|
github.com/gofiber/fiber/v2 v2.52.11/go.mod h1:YEcBbO/FB+5M1IZNBP9FO3J9281zgPAreiI1oqg8nDw=
|
||||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||||
@@ -137,16 +137,16 @@ github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD
|
|||||||
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||||
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
|
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
|
||||||
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||||
github.com/klauspost/compress v1.18.2 h1:iiPHWW0YrcFgpBYhsA6D1+fqHssJscY/Tm/y2Uqnapk=
|
github.com/klauspost/compress v1.18.3 h1:9PJRvfbmTabkOX8moIpXPbMMbYN60bWImDDU7L+/6zw=
|
||||||
github.com/klauspost/compress v1.18.2/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4=
|
github.com/klauspost/compress v1.18.3/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4=
|
||||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||||
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
||||||
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||||
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
|
github.com/lib/pq v1.11.1 h1:wuChtj2hfsGmmx3nf1m7xC2XpK6OtelS2shMY+bGMtI=
|
||||||
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
github.com/lib/pq v1.11.1/go.mod h1:/p+8NSbOcwzAEI7wiMXFlgydTwcgTr3OSKMsD2BitpA=
|
||||||
github.com/lufia/plan9stats v0.0.0-20251013123823-9fd1530e3ec3 h1:PwQumkgq4/acIiZhtifTV5OUqqiP82UAl0h87xj/l9k=
|
github.com/lufia/plan9stats v0.0.0-20251013123823-9fd1530e3ec3 h1:PwQumkgq4/acIiZhtifTV5OUqqiP82UAl0h87xj/l9k=
|
||||||
github.com/lufia/plan9stats v0.0.0-20251013123823-9fd1530e3ec3/go.mod h1:autxFIvghDt3jPTLoqZ9OZ7s9qTGNAWmYCjVFWPX/zg=
|
github.com/lufia/plan9stats v0.0.0-20251013123823-9fd1530e3ec3/go.mod h1:autxFIvghDt3jPTLoqZ9OZ7s9qTGNAWmYCjVFWPX/zg=
|
||||||
github.com/magiconair/properties v1.8.10 h1:s31yESBquKXCV9a/ScB3ESkOjUYYv+X0rg8SYxI99mE=
|
github.com/magiconair/properties v1.8.10 h1:s31yESBquKXCV9a/ScB3ESkOjUYYv+X0rg8SYxI99mE=
|
||||||
@@ -200,8 +200,8 @@ github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h
|
|||||||
github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg=
|
github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg=
|
||||||
github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
|
github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
|
||||||
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
|
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
|
||||||
github.com/prometheus/common v0.67.4 h1:yR3NqWO1/UyO1w2PhUvXlGQs/PtFmoveVO0KZ4+Lvsc=
|
github.com/prometheus/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTUGI4=
|
||||||
github.com/prometheus/common v0.67.4/go.mod h1:gP0fq6YjjNCLssJCQp0yk4M8W6ikLURwkdd/YKtTbyI=
|
github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxzaT3A2+HqjUw=
|
||||||
github.com/prometheus/procfs v0.19.2 h1:zUMhqEW66Ex7OXIiDkll3tl9a1ZdilUOd/F6ZXw4Vws=
|
github.com/prometheus/procfs v0.19.2 h1:zUMhqEW66Ex7OXIiDkll3tl9a1ZdilUOd/F6ZXw4Vws=
|
||||||
github.com/prometheus/procfs v0.19.2/go.mod h1:M0aotyiemPhBCM0z5w87kL22CxfcH05ZpYlu+b4J7mw=
|
github.com/prometheus/procfs v0.19.2/go.mod h1:M0aotyiemPhBCM0z5w87kL22CxfcH05ZpYlu+b4J7mw=
|
||||||
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
|
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
|
||||||
@@ -248,8 +248,8 @@ github.com/tklauser/numcpus v0.11.0 h1:nSTwhKH5e1dMNsCdVBukSZrURJRoHbSEQjdEbY+9R
|
|||||||
github.com/tklauser/numcpus v0.11.0/go.mod h1:z+LwcLq54uWZTX0u/bGobaV34u6V7KNlTZejzM6/3MQ=
|
github.com/tklauser/numcpus v0.11.0/go.mod h1:z+LwcLq54uWZTX0u/bGobaV34u6V7KNlTZejzM6/3MQ=
|
||||||
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||||
github.com/valyala/fasthttp v1.68.0 h1:v12Nx16iepr8r9ySOwqI+5RBJ/DqTxhOy1HrHoDFnok=
|
github.com/valyala/fasthttp v1.69.0 h1:fNLLESD2SooWeh2cidsuFtOcrEi4uB4m1mPrkJMZyVI=
|
||||||
github.com/valyala/fasthttp v1.68.0/go.mod h1:5EXiRfYQAoiO/khu4oU9VISC/eVY6JqmSpPJoHCKsz4=
|
github.com/valyala/fasthttp v1.69.0/go.mod h1:4wA4PfAraPlAsJ5jMSqCE2ug5tqUPwKXxVj8oNECGcw=
|
||||||
github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
|
github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
|
||||||
github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
|
github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
|
||||||
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
|
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
|
||||||
@@ -282,11 +282,11 @@ go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
|
|||||||
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU=
|
golang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8=
|
||||||
golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0=
|
golang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A=
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
|
golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU=
|
||||||
golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
|
golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY=
|
||||||
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
|
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
|
||||||
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
@@ -299,13 +299,13 @@ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
|
golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=
|
||||||
golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||||
golang.org/x/term v0.38.0 h1:PQ5pkm/rLO6HnxFR7N2lJHOZX6Kez5Y1gDSJla6jo7Q=
|
golang.org/x/term v0.39.0 h1:RclSuaJf32jOqZz74CkPA9qFuVTX7vhLlpfj/IGWlqY=
|
||||||
golang.org/x/term v0.38.0/go.mod h1:bSEAKrOT1W+VSu9TSCMtoGEOUcKxOKgl3LE5QEF/xVg=
|
golang.org/x/term v0.39.0/go.mod h1:yxzUCTP/U+FzoxfdKmLaA0RV1WgE0VY7hXBwKtY/4ww=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU=
|
golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE=
|
||||||
golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY=
|
golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8=
|
||||||
golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=
|
golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=
|
||||||
golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=
|
golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20251222181119-0a764e51fe1b h1:uA40e2M6fYRBf0+8uN5mLlqUtV192iiksiICIBkYJ1E=
|
google.golang.org/genproto/googleapis/api v0.0.0-20251222181119-0a764e51fe1b h1:uA40e2M6fYRBf0+8uN5mLlqUtV192iiksiICIBkYJ1E=
|
||||||
|
|||||||
@@ -19,4 +19,4 @@ sources:
|
|||||||
maintainers:
|
maintainers:
|
||||||
- name: Lukasz Raczylo
|
- name: Lukasz Raczylo
|
||||||
email: [email protected]
|
email: [email protected]
|
||||||
icon: https://raw.githubusercontent.com/lukaszraczylo/gohoarder/main/docs/logo.png
|
# icon: https://raw.githubusercontent.com/lukaszraczylo/gohoarder/main/docs/logo.png # TODO: Add logo
|
||||||
|
|||||||
@@ -93,24 +93,30 @@ data:
|
|||||||
low: {{ .Values.security.blockThresholds.low }}
|
low: {{ .Values.security.blockThresholds.low }}
|
||||||
scanners:
|
scanners:
|
||||||
trivy:
|
trivy:
|
||||||
enabled: {{ .Values.security.scanners.trivy.enabled }}
|
# Disabled in server config (no trivy binary), enabled via env var in scanner pod
|
||||||
|
enabled: false
|
||||||
timeout: {{ .Values.security.scanners.trivy.timeout | quote }}
|
timeout: {{ .Values.security.scanners.trivy.timeout | quote }}
|
||||||
cache_db: {{ .Values.security.scanners.trivy.cacheDb | quote }}
|
cache_db: {{ .Values.security.scanners.trivy.cacheDb | quote }}
|
||||||
osv:
|
osv:
|
||||||
|
# API-based scanner - works in both server and scanner pods
|
||||||
enabled: {{ .Values.security.scanners.osv.enabled }}
|
enabled: {{ .Values.security.scanners.osv.enabled }}
|
||||||
api_url: {{ .Values.security.scanners.osv.apiUrl | quote }}
|
api_url: {{ .Values.security.scanners.osv.apiUrl | quote }}
|
||||||
timeout: {{ .Values.security.scanners.osv.timeout | quote }}
|
timeout: {{ .Values.security.scanners.osv.timeout | quote }}
|
||||||
grype:
|
grype:
|
||||||
enabled: {{ .Values.security.scanners.grype.enabled }}
|
# Disabled in server config (no grype binary), enabled via env var in scanner pod
|
||||||
|
enabled: false
|
||||||
timeout: {{ .Values.security.scanners.grype.timeout | quote }}
|
timeout: {{ .Values.security.scanners.grype.timeout | quote }}
|
||||||
govulncheck:
|
govulncheck:
|
||||||
enabled: {{ .Values.security.scanners.govulncheck.enabled }}
|
# Disabled in server config (no go/govulncheck binary), enabled via env var in scanner pod
|
||||||
|
enabled: false
|
||||||
timeout: {{ .Values.security.scanners.govulncheck.timeout | quote }}
|
timeout: {{ .Values.security.scanners.govulncheck.timeout | quote }}
|
||||||
npm_audit:
|
npm_audit:
|
||||||
enabled: {{ .Values.security.scanners.npmAudit.enabled }}
|
# Disabled in server config (no npm binary), enabled via env var in scanner pod
|
||||||
|
enabled: false
|
||||||
timeout: {{ .Values.security.scanners.npmAudit.timeout | quote }}
|
timeout: {{ .Values.security.scanners.npmAudit.timeout | quote }}
|
||||||
pip_audit:
|
pip_audit:
|
||||||
enabled: {{ .Values.security.scanners.pipAudit.enabled }}
|
# Disabled in server config (no pip binary), enabled via env var in scanner pod
|
||||||
|
enabled: false
|
||||||
timeout: {{ .Values.security.scanners.pipAudit.timeout | quote }}
|
timeout: {{ .Values.security.scanners.pipAudit.timeout | quote }}
|
||||||
ghsa:
|
ghsa:
|
||||||
enabled: {{ .Values.security.scanners.ghsa.enabled }}
|
enabled: {{ .Values.security.scanners.ghsa.enabled }}
|
||||||
|
|||||||
@@ -67,11 +67,18 @@ spec:
|
|||||||
protocol: TCP
|
protocol: TCP
|
||||||
env:
|
env:
|
||||||
- name: API_BASE_URL
|
- name: API_BASE_URL
|
||||||
value: {{ .Values.frontend.backendUrl | default (printf "http://%s-server:%d" (include "gohoarder.fullname" .) (.Values.server.service.port | int)) | quote }}
|
value: {{ .Values.frontend.backendUrl | default "/api" | quote }}
|
||||||
- name: APP_VERSION
|
- name: APP_VERSION
|
||||||
value: {{ .Chart.AppVersion | quote }}
|
value: {{ .Chart.AppVersion | quote }}
|
||||||
- name: APP_NAME
|
- name: APP_NAME
|
||||||
value: "GoHoarder"
|
value: "GoHoarder"
|
||||||
|
# Backend proxy configuration (frontend now includes reverse proxy)
|
||||||
|
- name: BACKEND_HOST
|
||||||
|
value: {{ include "gohoarder.fullname" . }}-server
|
||||||
|
- name: BACKEND_PORT
|
||||||
|
value: {{ .Values.server.service.port | quote }}
|
||||||
|
- name: SERVER_NAME
|
||||||
|
value: {{ .Values.frontend.serverName | default "_" | quote }}
|
||||||
{{- with .Values.frontend.env }}
|
{{- with .Values.frontend.env }}
|
||||||
{{- toYaml . | nindent 8 }}
|
{{- toYaml . | nindent 8 }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
|
|||||||
@@ -109,6 +109,17 @@ spec:
|
|||||||
env:
|
env:
|
||||||
- name: CONFIG_FILE
|
- name: CONFIG_FILE
|
||||||
value: /etc/gohoarder/config.yaml
|
value: /etc/gohoarder/config.yaml
|
||||||
|
# Enable tool-based scanners only in scanner pod (server doesn't have the tools)
|
||||||
|
- name: GOHOARDER_SECURITY_SCANNERS_TRIVY_ENABLED
|
||||||
|
value: "{{ .Values.security.scanners.trivy.enabled }}"
|
||||||
|
- name: GOHOARDER_SECURITY_SCANNERS_GRYPE_ENABLED
|
||||||
|
value: "{{ .Values.security.scanners.grype.enabled }}"
|
||||||
|
- name: GOHOARDER_SECURITY_SCANNERS_GOVULNCHECK_ENABLED
|
||||||
|
value: "{{ .Values.security.scanners.govulncheck.enabled }}"
|
||||||
|
- name: GOHOARDER_SECURITY_SCANNERS_NPM_AUDIT_ENABLED
|
||||||
|
value: "{{ .Values.security.scanners.npmAudit.enabled }}"
|
||||||
|
- name: GOHOARDER_SECURITY_SCANNERS_PIP_AUDIT_ENABLED
|
||||||
|
value: "{{ .Values.security.scanners.pipAudit.enabled }}"
|
||||||
{{- if and (eq .Values.metadata.backend "postgresql") .Values.metadata.postgresql.existingSecret }}
|
{{- if and (eq .Values.metadata.backend "postgresql") .Values.metadata.postgresql.existingSecret }}
|
||||||
- name: POSTGRES_USER
|
- name: POSTGRES_USER
|
||||||
valueFrom:
|
valueFrom:
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
{{- if .Values.ingress.enabled -}}
|
{{- if .Values.ingress.enabled -}}
|
||||||
{{- if .Values.ingress.frontend.enabled -}}
|
|
||||||
apiVersion: networking.k8s.io/v1
|
apiVersion: networking.k8s.io/v1
|
||||||
kind: Ingress
|
kind: Ingress
|
||||||
metadata:
|
metadata:
|
||||||
name: {{ include "gohoarder.fullname" . }}-frontend
|
name: {{ include "gohoarder.fullname" . }}
|
||||||
labels:
|
labels:
|
||||||
{{- include "gohoarder.frontend.labels" . | nindent 4 }}
|
{{- include "gohoarder.labels" . | nindent 4 }}
|
||||||
{{- with .Values.ingress.annotations }}
|
{{- with .Values.ingress.annotations }}
|
||||||
annotations:
|
annotations:
|
||||||
{{- toYaml . | nindent 4 }}
|
{{- toYaml . | nindent 4 }}
|
||||||
@@ -14,65 +13,17 @@ spec:
|
|||||||
{{- if .Values.ingress.className }}
|
{{- if .Values.ingress.className }}
|
||||||
ingressClassName: {{ .Values.ingress.className }}
|
ingressClassName: {{ .Values.ingress.className }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- if .Values.ingress.frontend.tls.enabled }}
|
{{- if .Values.ingress.tls.enabled }}
|
||||||
tls:
|
tls:
|
||||||
- hosts:
|
- hosts:
|
||||||
- {{ .Values.ingress.frontend.host | default (printf "%s.%s" "gohoarder" .Values.global.domain) | quote }}
|
- {{ .Values.ingress.host | default (printf "%s.%s" "gohoarder" .Values.global.domain) | quote }}
|
||||||
secretName: {{ .Values.ingress.frontend.tls.secretName }}
|
secretName: {{ .Values.ingress.tls.secretName }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
rules:
|
rules:
|
||||||
- host: {{ .Values.ingress.frontend.host | default (printf "%s.%s" "gohoarder" .Values.global.domain) | quote }}
|
- host: {{ .Values.ingress.host | default (printf "%s.%s" "gohoarder" .Values.global.domain) | quote }}
|
||||||
http:
|
http:
|
||||||
paths:
|
paths:
|
||||||
- path: /npm
|
# Route all traffic to frontend (which now includes reverse proxy to backend)
|
||||||
pathType: Prefix
|
|
||||||
backend:
|
|
||||||
service:
|
|
||||||
name: {{ include "gohoarder.fullname" . }}-server
|
|
||||||
port:
|
|
||||||
number: {{ .Values.server.service.port }}
|
|
||||||
- path: /pypi
|
|
||||||
pathType: Prefix
|
|
||||||
backend:
|
|
||||||
service:
|
|
||||||
name: {{ include "gohoarder.fullname" . }}-server
|
|
||||||
port:
|
|
||||||
number: {{ .Values.server.service.port }}
|
|
||||||
- path: /go
|
|
||||||
pathType: Prefix
|
|
||||||
backend:
|
|
||||||
service:
|
|
||||||
name: {{ include "gohoarder.fullname" . }}-server
|
|
||||||
port:
|
|
||||||
number: {{ .Values.server.service.port }}
|
|
||||||
- path: /api
|
|
||||||
pathType: Prefix
|
|
||||||
backend:
|
|
||||||
service:
|
|
||||||
name: {{ include "gohoarder.fullname" . }}-server
|
|
||||||
port:
|
|
||||||
number: {{ .Values.server.service.port }}
|
|
||||||
- path: /ws
|
|
||||||
pathType: Prefix
|
|
||||||
backend:
|
|
||||||
service:
|
|
||||||
name: {{ include "gohoarder.fullname" . }}-server
|
|
||||||
port:
|
|
||||||
number: {{ .Values.server.service.port }}
|
|
||||||
- path: /health
|
|
||||||
pathType: Prefix
|
|
||||||
backend:
|
|
||||||
service:
|
|
||||||
name: {{ include "gohoarder.fullname" . }}-server
|
|
||||||
port:
|
|
||||||
number: {{ .Values.server.service.port }}
|
|
||||||
- path: /metrics
|
|
||||||
pathType: Prefix
|
|
||||||
backend:
|
|
||||||
service:
|
|
||||||
name: {{ include "gohoarder.fullname" . }}-server
|
|
||||||
port:
|
|
||||||
number: {{ .Values.server.service.port }}
|
|
||||||
- path: /
|
- path: /
|
||||||
pathType: Prefix
|
pathType: Prefix
|
||||||
backend:
|
backend:
|
||||||
@@ -81,38 +32,3 @@ spec:
|
|||||||
port:
|
port:
|
||||||
number: {{ .Values.frontend.service.port }}
|
number: {{ .Values.frontend.service.port }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
---
|
|
||||||
{{- if .Values.ingress.api.enabled }}
|
|
||||||
apiVersion: networking.k8s.io/v1
|
|
||||||
kind: Ingress
|
|
||||||
metadata:
|
|
||||||
name: {{ include "gohoarder.fullname" . }}-api
|
|
||||||
labels:
|
|
||||||
{{- include "gohoarder.server.labels" . | nindent 4 }}
|
|
||||||
{{- with .Values.ingress.annotations }}
|
|
||||||
annotations:
|
|
||||||
{{- toYaml . | nindent 4 }}
|
|
||||||
{{- end }}
|
|
||||||
spec:
|
|
||||||
{{- if .Values.ingress.className }}
|
|
||||||
ingressClassName: {{ .Values.ingress.className }}
|
|
||||||
{{- end }}
|
|
||||||
{{- if .Values.ingress.api.tls.enabled }}
|
|
||||||
tls:
|
|
||||||
- hosts:
|
|
||||||
- {{ .Values.ingress.api.host | default (printf "api.%s.%s" "gohoarder" .Values.global.domain) | quote }}
|
|
||||||
secretName: {{ .Values.ingress.api.tls.secretName }}
|
|
||||||
{{- end }}
|
|
||||||
rules:
|
|
||||||
- host: {{ .Values.ingress.api.host | default (printf "api.%s.%s" "gohoarder" .Values.global.domain) | quote }}
|
|
||||||
http:
|
|
||||||
paths:
|
|
||||||
- path: /
|
|
||||||
pathType: Prefix
|
|
||||||
backend:
|
|
||||||
service:
|
|
||||||
name: {{ include "gohoarder.fullname" . }}-server
|
|
||||||
port:
|
|
||||||
number: {{ .Values.server.service.port }}
|
|
||||||
{{- end }}
|
|
||||||
{{- end }}
|
|
||||||
|
|||||||
@@ -507,21 +507,12 @@ ingress:
|
|||||||
nginx.ingress.kubernetes.io/proxy-read-timeout: "300"
|
nginx.ingress.kubernetes.io/proxy-read-timeout: "300"
|
||||||
nginx.ingress.kubernetes.io/proxy-send-timeout: "300"
|
nginx.ingress.kubernetes.io/proxy-send-timeout: "300"
|
||||||
|
|
||||||
# Ingress for frontend
|
# Single ingress routes all traffic to frontend
|
||||||
frontend:
|
# Frontend now includes reverse proxy to backend (merged gateway functionality)
|
||||||
enabled: true
|
host: "gohoarder.local"
|
||||||
host: "gohoarder.local"
|
tls:
|
||||||
tls:
|
|
||||||
enabled: false
|
|
||||||
secretName: "gohoarder-frontend-tls"
|
|
||||||
|
|
||||||
# Ingress for API (if you want separate ingress)
|
|
||||||
api:
|
|
||||||
enabled: false
|
enabled: false
|
||||||
host: "api.gohoarder.local"
|
secretName: "gohoarder-tls"
|
||||||
tls:
|
|
||||||
enabled: false
|
|
||||||
secretName: "gohoarder-api-tls"
|
|
||||||
|
|
||||||
# Autoscaling configuration
|
# Autoscaling configuration
|
||||||
autoscaling:
|
autoscaling:
|
||||||
|
|||||||
+3
-1
@@ -290,7 +290,9 @@ func (a *App) initializeComponents() error {
|
|||||||
a.healthChecker.AddCheck("scanner", func(ctx context.Context) (health.Status, string) {
|
a.healthChecker.AddCheck("scanner", func(ctx context.Context) (health.Status, string) {
|
||||||
if a.config.Security.Enabled {
|
if a.config.Security.Enabled {
|
||||||
if err := a.scanManager.Health(ctx); err != nil {
|
if err := a.scanManager.Health(ctx); err != nil {
|
||||||
return health.StatusUnhealthy, err.Error()
|
// Scanner failures (e.g., API rate limits) shouldn't mark server as unhealthy
|
||||||
|
// Server can still serve cached packages, just can't scan new ones
|
||||||
|
return health.StatusDegraded, err.Error()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return health.StatusHealthy, ""
|
return health.StatusHealthy, ""
|
||||||
|
|||||||
Vendored
+47
-1
@@ -145,7 +145,37 @@ func (m *Manager) getOrFetch(ctx context.Context, registry, name, version string
|
|||||||
if err == nil {
|
if err == nil {
|
||||||
// Cache hit!
|
// Cache hit!
|
||||||
metrics.RecordCacheHit(registry)
|
metrics.RecordCacheHit(registry)
|
||||||
_ = m.metadata.UpdateDownloadCount(ctx, registry, name, version) // #nosec G104 -- Async update, error logged
|
|
||||||
|
// Update download count (log errors for debugging)
|
||||||
|
if err := m.metadata.UpdateDownloadCount(ctx, registry, name, version); err != nil {
|
||||||
|
log.Warn().
|
||||||
|
Err(err).
|
||||||
|
Str("registry", registry).
|
||||||
|
Str("package", name).
|
||||||
|
Str("version", version).
|
||||||
|
Msg("Failed to update download count - package may not exist in database")
|
||||||
|
|
||||||
|
// Try to save package to database if it doesn't exist
|
||||||
|
// This handles the case where storage has files but database was migrated/reset
|
||||||
|
if saveErr := m.metadata.SavePackage(ctx, pkg); saveErr != nil {
|
||||||
|
log.Error().
|
||||||
|
Err(saveErr).
|
||||||
|
Str("registry", registry).
|
||||||
|
Str("package", name).
|
||||||
|
Str("version", version).
|
||||||
|
Msg("Failed to save package to database")
|
||||||
|
} else {
|
||||||
|
// Retry download count update after saving package
|
||||||
|
if retryErr := m.metadata.UpdateDownloadCount(ctx, registry, name, version); retryErr != nil {
|
||||||
|
log.Error().
|
||||||
|
Err(retryErr).
|
||||||
|
Str("registry", registry).
|
||||||
|
Str("package", name).
|
||||||
|
Str("version", version).
|
||||||
|
Msg("Failed to update download count even after saving package")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Track download in analytics if enabled
|
// Track download in analytics if enabled
|
||||||
if m.analytics != nil {
|
if m.analytics != nil {
|
||||||
@@ -290,6 +320,22 @@ servePkg:
|
|||||||
return nil, errors.Wrap(err, errors.ErrCodeStorageFailure, "failed to retrieve just-stored package")
|
return nil, errors.Wrap(err, errors.ErrCodeStorageFailure, "failed to retrieve just-stored package")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Track download count for first-time download (cache miss)
|
||||||
|
// This ensures download count increments regardless of cache hit/miss
|
||||||
|
if err := m.metadata.UpdateDownloadCount(ctx, registry, name, version); err != nil {
|
||||||
|
log.Warn().
|
||||||
|
Err(err).
|
||||||
|
Str("registry", registry).
|
||||||
|
Str("package", name).
|
||||||
|
Str("version", version).
|
||||||
|
Msg("Failed to update download count for newly cached package")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Track download in analytics if enabled
|
||||||
|
if m.analytics != nil {
|
||||||
|
m.trackDownload(registry, name, version, storedPkg.Size)
|
||||||
|
}
|
||||||
|
|
||||||
return &CacheEntry{
|
return &CacheEntry{
|
||||||
Package: storedPkg,
|
Package: storedPkg,
|
||||||
Data: storedData,
|
Data: storedData,
|
||||||
|
|||||||
Vendored
+3
@@ -343,6 +343,7 @@ func TestGet(t *testing.T) {
|
|||||||
s.On("Put", mock.Anything, "npm/lodash/4.17.21", mock.Anything, mock.Anything).Return(nil)
|
s.On("Put", mock.Anything, "npm/lodash/4.17.21", mock.Anything, mock.Anything).Return(nil)
|
||||||
m.On("SavePackage", mock.Anything, mock.Anything).Return(nil)
|
m.On("SavePackage", mock.Anything, mock.Anything).Return(nil)
|
||||||
s.On("Get", mock.Anything, "npm/lodash/4.17.21").Return(io.NopCloser(strings.NewReader("upstream data")), nil)
|
s.On("Get", mock.Anything, "npm/lodash/4.17.21").Return(io.NopCloser(strings.NewReader("upstream data")), nil)
|
||||||
|
m.On("UpdateDownloadCount", mock.Anything, "npm", "lodash", "4.17.21").Return(nil)
|
||||||
},
|
},
|
||||||
fetchFunc: func(ctx context.Context) (io.ReadCloser, string, error) {
|
fetchFunc: func(ctx context.Context) (io.ReadCloser, string, error) {
|
||||||
return io.NopCloser(strings.NewReader("upstream data")), "https://registry.npmjs.org/lodash", nil
|
return io.NopCloser(strings.NewReader("upstream data")), "https://registry.npmjs.org/lodash", nil
|
||||||
@@ -374,6 +375,7 @@ func TestGet(t *testing.T) {
|
|||||||
s.On("Put", mock.Anything, "npm/expired-pkg/1.0.0", mock.Anything, mock.Anything).Return(nil)
|
s.On("Put", mock.Anything, "npm/expired-pkg/1.0.0", mock.Anything, mock.Anything).Return(nil)
|
||||||
m.On("SavePackage", mock.Anything, mock.Anything).Return(nil)
|
m.On("SavePackage", mock.Anything, mock.Anything).Return(nil)
|
||||||
s.On("Get", mock.Anything, "npm/expired-pkg/1.0.0").Return(io.NopCloser(strings.NewReader("refreshed data")), nil)
|
s.On("Get", mock.Anything, "npm/expired-pkg/1.0.0").Return(io.NopCloser(strings.NewReader("refreshed data")), nil)
|
||||||
|
m.On("UpdateDownloadCount", mock.Anything, "npm", "expired-pkg", "1.0.0").Return(nil)
|
||||||
},
|
},
|
||||||
fetchFunc: func(ctx context.Context) (io.ReadCloser, string, error) {
|
fetchFunc: func(ctx context.Context) (io.ReadCloser, string, error) {
|
||||||
return io.NopCloser(strings.NewReader("refreshed data")), "https://registry.npmjs.org/expired-pkg", nil
|
return io.NopCloser(strings.NewReader("refreshed data")), "https://registry.npmjs.org/expired-pkg", nil
|
||||||
@@ -435,6 +437,7 @@ func TestGet(t *testing.T) {
|
|||||||
m.On("SavePackage", mock.Anything, mock.Anything).Return(nil)
|
m.On("SavePackage", mock.Anything, mock.Anything).Return(nil)
|
||||||
// Second Get succeeds (after re-storing)
|
// Second Get succeeds (after re-storing)
|
||||||
s.On("Get", mock.Anything, "npm/inconsistent/1.0.0").Return(io.NopCloser(strings.NewReader("recovered data")), nil).Once()
|
s.On("Get", mock.Anything, "npm/inconsistent/1.0.0").Return(io.NopCloser(strings.NewReader("recovered data")), nil).Once()
|
||||||
|
m.On("UpdateDownloadCount", mock.Anything, "npm", "inconsistent", "1.0.0").Return(nil)
|
||||||
},
|
},
|
||||||
fetchFunc: func(ctx context.Context) (io.ReadCloser, string, error) {
|
fetchFunc: func(ctx context.Context) (io.ReadCloser, string, error) {
|
||||||
return io.NopCloser(strings.NewReader("recovered data")), "https://registry.npmjs.org/inconsistent", nil
|
return io.NopCloser(strings.NewReader("recovered data")), "https://registry.npmjs.org/inconsistent", nil
|
||||||
|
|||||||
@@ -583,29 +583,38 @@ func (s *GORMStoreV2) GetTimeSeriesStats(ctx context.Context, period string, reg
|
|||||||
// Determine which table to query based on period
|
// Determine which table to query based on period
|
||||||
var tableName string
|
var tableName string
|
||||||
var since time.Time
|
var since time.Time
|
||||||
|
var bucketDuration time.Duration
|
||||||
|
|
||||||
switch period {
|
switch period {
|
||||||
case "1h":
|
case "1h":
|
||||||
tableName = "download_stats_hourly"
|
tableName = "download_stats_hourly"
|
||||||
since = time.Now().Add(-1 * time.Hour)
|
since = time.Now().Add(-1 * time.Hour).Truncate(time.Hour)
|
||||||
|
bucketDuration = time.Hour
|
||||||
case "1day":
|
case "1day":
|
||||||
tableName = "download_stats_hourly"
|
tableName = "download_stats_hourly"
|
||||||
since = time.Now().Add(-24 * time.Hour)
|
since = time.Now().Add(-24 * time.Hour).Truncate(time.Hour)
|
||||||
|
bucketDuration = time.Hour
|
||||||
case "7day":
|
case "7day":
|
||||||
tableName = "download_stats_daily"
|
tableName = "download_stats_daily"
|
||||||
since = time.Now().Add(-7 * 24 * time.Hour)
|
since = time.Now().Add(-7 * 24 * time.Hour).Truncate(24 * time.Hour)
|
||||||
|
bucketDuration = 24 * time.Hour
|
||||||
case "30day":
|
case "30day":
|
||||||
tableName = "download_stats_daily"
|
tableName = "download_stats_daily"
|
||||||
since = time.Now().Add(-30 * 24 * time.Hour)
|
since = time.Now().Add(-30 * 24 * time.Hour).Truncate(24 * time.Hour)
|
||||||
|
bucketDuration = 24 * time.Hour
|
||||||
default:
|
default:
|
||||||
tableName = "download_stats_hourly"
|
tableName = "download_stats_hourly"
|
||||||
since = time.Now().Add(-24 * time.Hour)
|
since = time.Now().Add(-24 * time.Hour).Truncate(time.Hour)
|
||||||
|
bucketDuration = time.Hour
|
||||||
}
|
}
|
||||||
|
|
||||||
|
now := time.Now()
|
||||||
|
|
||||||
|
// Try to get registry-level aggregate stats first
|
||||||
query := s.db.WithContext(ctx).
|
query := s.db.WithContext(ctx).
|
||||||
Table(tableName).
|
Table(tableName).
|
||||||
Select("time_bucket as timestamp, download_count as value").
|
Select("time_bucket as timestamp, download_count as value").
|
||||||
Where("time_bucket >= ?", since)
|
Where("time_bucket >= ? AND time_bucket <= ?", since, now)
|
||||||
|
|
||||||
// Filter by registry if specified
|
// Filter by registry if specified
|
||||||
if registry != "" {
|
if registry != "" {
|
||||||
@@ -630,10 +639,45 @@ func (s *GORMStoreV2) GetTimeSeriesStats(ctx context.Context, period string, reg
|
|||||||
return nil, errors.Wrap(err, errors.ErrCodeStorageFailure, "failed to get time series stats")
|
return nil, errors.Wrap(err, errors.ErrCodeStorageFailure, "failed to get time series stats")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If no aggregate data found, try summing package-level stats
|
||||||
|
if len(results) == 0 {
|
||||||
|
sumQuery := s.db.WithContext(ctx).
|
||||||
|
Table(tableName).
|
||||||
|
Select("time_bucket as timestamp, SUM(download_count) as value").
|
||||||
|
Where("time_bucket >= ? AND time_bucket <= ?", since, now)
|
||||||
|
|
||||||
|
if registry != "" {
|
||||||
|
registryID, err := s.getRegistryID(registry)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
sumQuery = sumQuery.Where("registry_id = ? AND package_id IS NOT NULL", registryID)
|
||||||
|
} else {
|
||||||
|
sumQuery = sumQuery.Where("package_id IS NOT NULL")
|
||||||
|
}
|
||||||
|
|
||||||
|
sumQuery = sumQuery.Group("time_bucket").Order("time_bucket ASC")
|
||||||
|
|
||||||
|
if err := sumQuery.Scan(&results).Error; err != nil {
|
||||||
|
return nil, errors.Wrap(err, errors.ErrCodeStorageFailure, "failed to get time series stats from package data")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a map of existing data points
|
||||||
|
dataMap := make(map[time.Time]int64)
|
||||||
for _, r := range results {
|
for _, r := range results {
|
||||||
|
dataMap[r.Timestamp] = r.Value
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate continuous time series with 0 values for missing periods
|
||||||
|
for t := since; t.Before(now) || t.Equal(now); t = t.Add(bucketDuration) {
|
||||||
|
value := int64(0)
|
||||||
|
if v, exists := dataMap[t]; exists {
|
||||||
|
value = v
|
||||||
|
}
|
||||||
stats.DataPoints = append(stats.DataPoints, &metadata.TimeSeriesDataPoint{
|
stats.DataPoints = append(stats.DataPoints, &metadata.TimeSeriesDataPoint{
|
||||||
Timestamp: r.Timestamp,
|
Timestamp: t,
|
||||||
Value: r.Value,
|
Value: value,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -107,11 +107,17 @@ func (s *Scanner) Health(ctx context.Context) error {
|
|||||||
}
|
}
|
||||||
defer resp.Body.Close() // #nosec G104 -- Cleanup, error not critical
|
defer resp.Body.Close() // #nosec G104 -- Cleanup, error not critical
|
||||||
|
|
||||||
if resp.StatusCode != http.StatusOK {
|
// Accept any 2xx or 403 (rate limit) as healthy
|
||||||
return fmt.Errorf("github api returned status: %d", resp.StatusCode)
|
// Rate limits are expected without a GitHub token and shouldn't fail health checks
|
||||||
|
if resp.StatusCode >= 200 && resp.StatusCode < 300 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if resp.StatusCode == http.StatusForbidden {
|
||||||
|
log.Debug().Msg("GitHub API rate limited (expected without token)")
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return fmt.Errorf("github api returned status: %d", resp.StatusCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
// mapRegistryToEcosystem maps our registry names to GitHub ecosystem names
|
// mapRegistryToEcosystem maps our registry names to GitHub ecosystem names
|
||||||
|
|||||||
+52
-2
@@ -121,13 +121,17 @@ func (s *Scanner) Scan(ctx context.Context, registry, packageName, version strin
|
|||||||
// Convert registry to OSV ecosystem
|
// Convert registry to OSV ecosystem
|
||||||
ecosystem := s.registryToEcosystem(registry)
|
ecosystem := s.registryToEcosystem(registry)
|
||||||
|
|
||||||
|
// Clean package name and version for Go modules
|
||||||
|
// Go proxy cache keys include /@v/version.suffix which OSV doesn't understand
|
||||||
|
cleanName, cleanVersion := s.cleanGoModuleName(packageName, version, ecosystem)
|
||||||
|
|
||||||
// Build request
|
// Build request
|
||||||
req := OSVRequest{
|
req := OSVRequest{
|
||||||
Package: PackageInfo{
|
Package: PackageInfo{
|
||||||
Name: packageName,
|
Name: cleanName,
|
||||||
Ecosystem: ecosystem,
|
Ecosystem: ecosystem,
|
||||||
},
|
},
|
||||||
Version: version,
|
Version: cleanVersion,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Marshal request
|
// Marshal request
|
||||||
@@ -199,6 +203,52 @@ func (s *Scanner) registryToEcosystem(registry string) string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// cleanGoModuleName cleans Go module cache keys to extract the actual module path and version
|
||||||
|
// Go proxy cache keys include /@v/version.suffix patterns that need to be cleaned
|
||||||
|
// Examples:
|
||||||
|
// - "gorm.io/driver/sqlite/@v/v1.6.0.zip" -> "gorm.io/driver/sqlite", "v1.6.0"
|
||||||
|
// - "github.com/pkg/errors/@v/v0.9.1.mod" -> "github.com/pkg/errors", "v0.9.1"
|
||||||
|
// - "regular-package" -> "regular-package", "version" (unchanged for non-Go)
|
||||||
|
func (s *Scanner) cleanGoModuleName(packageName, version, ecosystem string) (string, string) {
|
||||||
|
// Only clean for Go modules
|
||||||
|
if ecosystem != "Go" {
|
||||||
|
return packageName, version
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if package name contains /@v/ pattern (Go module proxy format)
|
||||||
|
if strings.Contains(packageName, "/@v/") {
|
||||||
|
// Split on /@v/ to get the module path
|
||||||
|
parts := strings.Split(packageName, "/@v/")
|
||||||
|
if len(parts) == 2 {
|
||||||
|
// parts[0] is the clean module path (e.g., "gorm.io/driver/sqlite")
|
||||||
|
// parts[1] might be "v1.6.0.zip" or "v1.6.0.mod" or "v1.6.0.info"
|
||||||
|
cleanName := parts[0]
|
||||||
|
|
||||||
|
// Extract version from the second part if version wasn't already clean
|
||||||
|
// Remove file suffixes like .zip, .mod, .info
|
||||||
|
versionPart := parts[1]
|
||||||
|
versionPart = strings.TrimSuffix(versionPart, ".zip")
|
||||||
|
versionPart = strings.TrimSuffix(versionPart, ".mod")
|
||||||
|
versionPart = strings.TrimSuffix(versionPart, ".info")
|
||||||
|
|
||||||
|
// Use the extracted version if it looks valid, otherwise use the provided version
|
||||||
|
if versionPart != "" && strings.HasPrefix(versionPart, "v") {
|
||||||
|
return cleanName, versionPart
|
||||||
|
}
|
||||||
|
|
||||||
|
return cleanName, version
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Also clean version of any file suffixes
|
||||||
|
cleanVersion := version
|
||||||
|
cleanVersion = strings.TrimSuffix(cleanVersion, ".zip")
|
||||||
|
cleanVersion = strings.TrimSuffix(cleanVersion, ".mod")
|
||||||
|
cleanVersion = strings.TrimSuffix(cleanVersion, ".info")
|
||||||
|
|
||||||
|
return packageName, cleanVersion
|
||||||
|
}
|
||||||
|
|
||||||
// convertOSVResult converts OSV response to metadata.ScanResult
|
// convertOSVResult converts OSV response to metadata.ScanResult
|
||||||
func (s *Scanner) convertOSVResult(osvResp *OSVResponse, registry, packageName, version string) *metadata.ScanResult {
|
func (s *Scanner) convertOSVResult(osvResp *OSVResponse, registry, packageName, version string) *metadata.ScanResult {
|
||||||
vulnerabilities := make([]metadata.Vulnerability, 0, len(osvResp.Vulns))
|
vulnerabilities := make([]metadata.Vulnerability, 0, len(osvResp.Vulns))
|
||||||
|
|||||||
Executable
+78
@@ -0,0 +1,78 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Workflow prepare script for CI/CD
|
||||||
|
# Installs CGO dependencies for SQLite support
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
echo "=== GoHoarder Workflow Prepare ==="
|
||||||
|
echo "Host OS: $(uname -s)"
|
||||||
|
echo "Target GOOS: ${TARGET_GOOS:-auto}"
|
||||||
|
echo "Target GOARCH: ${TARGET_GOARCH:-auto}"
|
||||||
|
|
||||||
|
# Detect host OS
|
||||||
|
HOST_OS=$(uname -s | tr '[:upper:]' '[:lower:]')
|
||||||
|
|
||||||
|
# Install SQLite development headers based on platform
|
||||||
|
case "$HOST_OS" in
|
||||||
|
linux*)
|
||||||
|
echo "Installing SQLite development headers for Linux..."
|
||||||
|
if command -v apt-get &> /dev/null; then
|
||||||
|
# Ubuntu/Debian
|
||||||
|
sudo apt-get update -qq
|
||||||
|
sudo apt-get install -y -qq libsqlite3-dev
|
||||||
|
elif command -v yum &> /dev/null; then
|
||||||
|
# RHEL/CentOS
|
||||||
|
sudo yum install -y sqlite-devel
|
||||||
|
elif command -v apk &> /dev/null; then
|
||||||
|
# Alpine
|
||||||
|
sudo apk add --no-cache sqlite-dev
|
||||||
|
fi
|
||||||
|
echo "✓ SQLite headers installed"
|
||||||
|
;;
|
||||||
|
|
||||||
|
darwin*)
|
||||||
|
echo "Installing SQLite for macOS..."
|
||||||
|
# macOS usually has SQLite via Xcode Command Line Tools
|
||||||
|
# but ensure it's available via Homebrew if needed
|
||||||
|
if ! pkg-config --exists sqlite3; then
|
||||||
|
brew install sqlite3
|
||||||
|
fi
|
||||||
|
echo "✓ SQLite available"
|
||||||
|
;;
|
||||||
|
|
||||||
|
mingw*|msys*|cygwin*)
|
||||||
|
echo "Installing SQLite for Windows..."
|
||||||
|
# Download SQLite amalgamation for Windows
|
||||||
|
SQLITE_VERSION="3470200"
|
||||||
|
SQLITE_YEAR="2024"
|
||||||
|
SQLITE_DIR="/c/sqlite"
|
||||||
|
SQLITE_URL="https://www.sqlite.org/${SQLITE_YEAR}/sqlite-amalgamation-${SQLITE_VERSION}.zip"
|
||||||
|
|
||||||
|
mkdir -p "$SQLITE_DIR"
|
||||||
|
curl -sSL "$SQLITE_URL" -o /tmp/sqlite.zip
|
||||||
|
unzip -q /tmp/sqlite.zip -d /tmp/
|
||||||
|
cp /tmp/sqlite-amalgamation-${SQLITE_VERSION}/* "$SQLITE_DIR/"
|
||||||
|
rm -rf /tmp/sqlite.zip /tmp/sqlite-amalgamation-${SQLITE_VERSION}
|
||||||
|
|
||||||
|
echo "CGO_CFLAGS=-I${SQLITE_DIR}" >> "$GITHUB_ENV"
|
||||||
|
echo "CGO_LDFLAGS=-L${SQLITE_DIR}" >> "$GITHUB_ENV"
|
||||||
|
echo "✓ SQLite setup complete"
|
||||||
|
;;
|
||||||
|
|
||||||
|
*)
|
||||||
|
echo "Unknown OS: $HOST_OS - skipping SQLite setup"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# Verify SQLite is available
|
||||||
|
echo ""
|
||||||
|
echo "=== Verifying SQLite availability ==="
|
||||||
|
if pkg-config --exists sqlite3; then
|
||||||
|
echo "✓ SQLite pkg-config found"
|
||||||
|
pkg-config --modversion sqlite3
|
||||||
|
else
|
||||||
|
echo "⚠ SQLite pkg-config not found (may still work via system headers)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "=== Workflow prepare complete ==="
|
||||||
Reference in New Issue
Block a user