diff --git a/.env.example b/.env.example index 0e0d628f..5a052dbb 100644 --- a/.env.example +++ b/.env.example @@ -1 +1,2 @@ JWT_SECRET='your-secret-key-here' +DATA_DIR=/home/your-user/hypeman/.datadir # or leave unset to default to /var/lib/hypeman diff --git a/.gitignore b/.gitignore index 1d7185cd..7a77684c 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,4 @@ bin/** .env tmp tmp/** +.datadir diff --git a/Makefile b/Makefile index 0412ccc5..aa985157 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ SHELL := /bin/bash -.PHONY: oapi-generate generate-wire generate-all dev build test install-tools +.PHONY: oapi-generate generate-wire generate-all dev build test install-tools gen-jwt # Directory where local binaries will be installed BIN_DIR ?= $(CURDIR)/bin @@ -11,6 +11,7 @@ $(BIN_DIR): OAPI_CODEGEN ?= $(BIN_DIR)/oapi-codegen AIR ?= $(BIN_DIR)/air WIRE ?= $(BIN_DIR)/wire +GODOTENV ?= $(BIN_DIR)/godotenv # Install oapi-codegen $(OAPI_CODEGEN): | $(BIN_DIR) @@ -24,7 +25,11 @@ $(AIR): | $(BIN_DIR) $(WIRE): | $(BIN_DIR) GOBIN=$(BIN_DIR) go install github.com/google/wire/cmd/wire@latest -install-tools: $(OAPI_CODEGEN) $(AIR) $(WIRE) +# Install godotenv for loading .env files +$(GODOTENV): | $(BIN_DIR) + GOBIN=$(BIN_DIR) go install github.com/joho/godotenv/cmd/godotenv@latest + +install-tools: $(OAPI_CODEGEN) $(AIR) $(WIRE) $(GODOTENV) # Generate Go code from OpenAPI spec oapi-generate: $(OAPI_CODEGEN) @@ -53,6 +58,11 @@ dev: $(AIR) test: go test -tags containers_image_openpgp -v -timeout 30s ./... +# Generate JWT token for testing +# Usage: make gen-jwt [USER_ID=test-user] +gen-jwt: $(GODOTENV) + @$(GODOTENV) -f .env go run ./cmd/gen-jwt -user-id $${USER_ID:-test-user} + # Clean generated files and binaries clean: rm -rf $(BIN_DIR) diff --git a/README.md b/README.md index 771d2d58..0320d54f 100644 --- a/README.md +++ b/README.md @@ -49,7 +49,18 @@ make build ``` ### Running the Server -Start the server with hot-reload for development: +1. Copy the example environment file and modify the values: +```bash +cp .env.example .env +# Edit .env and set JWT_SECRET and other configuration values +``` + +2. Generate a JWT token for testing (optional): +```bash +make gen-jwt +``` + +3. Start the server with hot-reload for development: ```bash make dev ``` diff --git a/cmd/api/api/images.go b/cmd/api/api/images.go index b1cd4b60..913b9918 100644 --- a/cmd/api/api/images.go +++ b/cmd/api/api/images.go @@ -20,12 +20,12 @@ func (s *ApiService) ListImages(ctx context.Context, request oapi.ListImagesRequ Message: "failed to list images", }, nil } - + oapiImages := make([]oapi.Image, len(domainImages)) for i, img := range domainImages { oapiImages[i] = imageToOAPI(img) } - + return oapi.ListImages200JSONResponse(oapiImages), nil } diff --git a/cmd/gen-jwt/main.go b/cmd/gen-jwt/main.go new file mode 100644 index 00000000..a14cd409 --- /dev/null +++ b/cmd/gen-jwt/main.go @@ -0,0 +1,34 @@ +package main + +import ( + "flag" + "fmt" + "os" + "time" + + "github.com/golang-jwt/jwt/v5" +) + +func main() { + jwtSecret := os.Getenv("JWT_SECRET") + if jwtSecret == "" { + fmt.Fprintf(os.Stderr, "Error: JWT_SECRET environment variable is not set\n") + os.Exit(1) + } + userID := flag.String("user-id", "test-user", "User ID to include in the JWT token") + flag.Parse() + + claims := jwt.MapClaims{ + "sub": *userID, + "iat": time.Now().Unix(), + "exp": time.Now().Add(24 * time.Hour).Unix(), + } + token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) + tokenString, err := token.SignedString([]byte(jwtSecret)) + if err != nil { + fmt.Fprintf(os.Stderr, "Error generating token: %v\n", err) + os.Exit(1) + } + + fmt.Println(tokenString) +} diff --git a/lib/images/oci.go b/lib/images/oci.go index 645b174d..7063f1a1 100644 --- a/lib/images/oci.go +++ b/lib/images/oci.go @@ -11,7 +11,7 @@ import ( "github.com/google/go-containerregistry/pkg/v1/empty" "github.com/google/go-containerregistry/pkg/v1/layout" "github.com/google/go-containerregistry/pkg/v1/remote" - "github.com/opencontainers/image-spec/specs-go/v1" + v1 "github.com/opencontainers/image-spec/specs-go/v1" rspec "github.com/opencontainers/runtime-spec/specs-go" "github.com/opencontainers/umoci/oci/cas/dir" "github.com/opencontainers/umoci/oci/casext" @@ -70,7 +70,7 @@ func (c *ociClient) inspectManifest(ctx context.Context, imageRef string) (strin // Use system authentication (reads from ~/.docker/config.json, etc.) // Default retry: only on network errors, max ~1.3s total - descriptor, err := remote.Head(ref, + descriptor, err := remote.Head(ref, remote.WithContext(ctx), remote.WithAuthFromKeychain(authn.DefaultKeychain)) if err != nil { @@ -126,7 +126,7 @@ func (c *ociClient) pullToOCILayout(ctx context.Context, imageRef, layoutTag str // Use system authentication (reads from ~/.docker/config.json, etc.) // Default retry: only on network errors, max ~1.3s total - img, err := remote.Image(ref, + img, err := remote.Image(ref, remote.WithContext(ctx), remote.WithAuthFromKeychain(authn.DefaultKeychain)) if err != nil { @@ -293,7 +293,7 @@ func (c *ociClient) unpackLayers(ctx context.Context, imageRef, targetDir string // Map container UIDs to current user's UID (identity mapping) uid := uint32(os.Getuid()) gid := uint32(os.Getgid()) - + unpackOpts := &layer.UnpackOptions{ OnDiskFormat: layer.DirRootfs{ MapOptions: layer.MapOptions{ @@ -307,7 +307,7 @@ func (c *ociClient) unpackLayers(ctx context.Context, imageRef, targetDir string }, }, } - + err = layer.UnpackRootfs(context.Background(), casEngine, targetDir, manifest, unpackOpts) if err != nil { return fmt.Errorf("unpack rootfs: %w", err) @@ -322,4 +322,3 @@ type containerMetadata struct { Env map[string]string WorkingDir string } -