diff --git a/.github/assets/access-control.png b/.github/assets/access-control.png new file mode 100644 index 0000000..37c5e8f Binary files /dev/null and b/.github/assets/access-control.png differ diff --git a/.github/assets/buckets.png b/.github/assets/buckets.png new file mode 100644 index 0000000..f2509a1 Binary files /dev/null and b/.github/assets/buckets.png differ diff --git a/.github/assets/cluster.png b/.github/assets/cluster.png new file mode 100644 index 0000000..d6a12de Binary files /dev/null and b/.github/assets/cluster.png differ diff --git a/.github/assets/dashboard.png b/.github/assets/dashboard.png new file mode 100644 index 0000000..35e9b77 Binary files /dev/null and b/.github/assets/dashboard.png differ diff --git a/README.md b/README.md index 64da512..ee2e521 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Garage UI -A modern, full-stack web interface for managing [Garage](https://garagehq.deuxfleurs.fr/) distributed object storage systems. Built with React, TypeScript, Go, and Fiber for production-ready S3-compatible storage management. +A web interface for managing [Garage](https://garagehq.deuxfleurs.fr/) object storage clusters. [![Docker Build](https://github.com/Noooste/garage-ui/actions/workflows/build.yml/badge.svg)](https://github.com/Noooste/garage-ui/actions/workflows/build.yml) [![Helm Chart](https://github.com/Noooste/garage-ui/actions/workflows/release.yml/badge.svg)](https://github.com/Noooste/garage-ui/actions/workflows/release.yml) @@ -9,665 +9,188 @@ A modern, full-stack web interface for managing [Garage](https://garagehq.deuxfl [![Node Version](https://img.shields.io/badge/Node-25%2B-339933?logo=node.js)](https://nodejs.org/) [![Artifact Hub](https://img.shields.io/endpoint?url=https://artifacthub.io/badge/repository/garage-ui)](https://artifacthub.io/packages/search?repo=garage-ui) -Garage UI provides a comprehensive dashboard for managing your Garage S3 storage cluster, featuring bucket management, object operations, user access control, and real-time cluster monitoring—all through an intuitive web interface. - --- + + + + + + + + + +
DashboardBuckets
ClusterAccess Control
+ ## Features -- **Bucket Management** - Create, configure, and delete S3 buckets with visual controls -- **Object Operations** - Upload, download, and manage objects with drag-and-drop support -- **User & Access Control** - Manage access keys, permissions, and user credentials -- **Cluster Monitoring** - Real-time cluster health metrics, node status, and performance statistics -- **Multi-Auth Support** - Flexible authentication with None, Basic Auth, and OIDC/OAuth2 modes -- **Modern UI/UX** - Responsive React interface with dark mode, built on Tailwind CSS and shadcn/ui -- **Production Ready** - Docker/Kubernetes deployment, health checks, and Prometheus metrics -- **API-First Design** - RESTful API with Swagger/OpenAPI documentation - ---- +- Bucket and object management +- User access control +- Cluster monitoring +- Multiple authentication options (none/basic/OIDC) +- Drag-and-drop file uploads ## Quick Start ### Prerequisites -- **Garage S3** storage cluster running (v2.0.0+) -- **Docker** & Docker Compose (recommended) OR -- **Go** 1.25+ and **Node.js** 25+ (for development) +- Docker & Docker Compose +- Garage S3 cluster (v2.1.0+) or use the included setup -### Using Docker Compose (Recommended) - -1. **Clone the repository:** +### 1. Clone & Setup ```bash git clone https://github.com/Noooste/garage-ui.git cd garage-ui ``` -2. **Configure the application:** +### 2. Start Garage + +If you don't have Garage running: + +```bash +docker-compose up -d garage +sleep 10 + +# Initialize cluster +docker-compose exec garage garage layout assign -z dc1 -c 1G $(docker-compose exec garage garage node id -q) +docker-compose exec garage garage layout apply --version 1 + +# Create admin key +docker-compose exec garage garage key create admin-key +``` + +Save the access key and secret key from the output. + +### 3. Configure ```bash cp config.yaml.example config.yaml -# Edit config.yaml with your Garage endpoints and credentials ``` -3. **Start the services:** +Edit `config.yaml` with your Garage endpoints and admin token (from `garage.toml`). + +### 4. Start UI ```bash -docker-compose up -d +docker-compose up -d garage-ui ``` -4. **Access the UI:** - -Open your browser to [http://localhost:8080](http://localhost:8080) - -> The Docker Compose setup includes both Garage and Garage UI services for quick local development. - -### Using Docker - -```bash -docker run -d \ - -p 8080:8080 \ - -v $(pwd)/config.yaml:/app/config.yaml \ - -e GARAGE_UI_GARAGE_ENDPOINT=http://your-garage:3900 \ - -e GARAGE_UI_GARAGE_ADMIN_ENDPOINT=http://your-garage:3903 \ - noooste/garage-ui:latest -``` - ---- - -## Installation - -### Docker Deployment - -**Pull the latest image:** - -```bash -docker pull noooste/garage-ui:latest -``` - -The Docker image is multi-platform, supporting `linux/amd64` and `linux/arm64`. - -### Kubernetes Deployment with Helm - -```bash -# Add the Helm repository (if available) -helm repo add garage-ui https://helm.noste.dev/ - -# Install the chart -helm install garage-ui garage-ui/garage-ui \ - --set garage.endpoint=http://garage:3900 \ - --set garage.adminEndpoint=http://garage:3903 \ - --set garage.adminToken=your-admin-token -``` - -Or use the local Helm chart: - -```bash -helm install garage-ui ./helm/garage-ui -f custom-values.yaml -``` - -### Building from Source - -**Backend:** - -```bash -cd backend -go mod download -swag init # Generate API docs -go build -o garage-ui . -``` - -**Frontend:** - -```bash -cd frontend -npm install -npm run build -``` - -**Run:** - -```bash -# From project root -./backend/garage-ui --config config.yaml -``` - ---- +Access at http://localhost:8080 ## Configuration -Garage UI can be configured via YAML file or environment variables. - -### Configuration File - -Create a `config.yaml` based on the provided example: +Minimum required config: ```yaml -# Server configuration server: - host: "0.0.0.0" port: 8080 - environment: "production" -# Garage S3 Configuration garage: - endpoint: "garage:3900" - region: "eu-west-1" # Ensure to match Garage region from garage.toml + endpoint: "http://garage:3900" admin_endpoint: "http://garage:3903" - admin_token: "your-admin-token-here" - -# Authentication Configuration -# You can enable one or both authentication methods -auth: - # Admin authentication (username/password) - admin: - enabled: false # Set to true to enable admin login - username: "admin" - password: "secure-password" - - # OIDC authentication (Keycloak, Auth0, etc.) - oidc: - enabled: false # Set to true to enable OIDC login - provider_name: "Keycloak" - client_id: "garage-ui" - client_secret: "your-client-secret" - issuer_url: "https://auth.example.com/realms/master" - auth_url: "https://auth.example.com/realms/master/protocol/openid-connect/auth" - token_url: "https://auth.example.com/realms/master/protocol/openid-connect/token" + admin_token: "your-admin-token" + region: "garage" ``` -See [`config.yaml.example`](config.yaml.example) for all available options. +Enable authentication (optional): + +```yaml +auth: + admin: + enabled: true + username: "admin" + password: "your-password" +``` + +See [config.yaml.example](config.yaml.example) for all options. ### Environment Variables -All configuration can be set via environment variables with the prefix `GARAGE_UI_`: +Override any config value with `GARAGE_UI_` prefix: ```bash GARAGE_UI_SERVER_PORT=8080 GARAGE_UI_GARAGE_ENDPOINT=http://garage:3900 -GARAGE_UI_GARAGE_ADMIN_ENDPOINT=http://garage:3903 GARAGE_UI_GARAGE_ADMIN_TOKEN=your-token -GARAGE_UI_AUTH_ADMIN_ENABLED=true -GARAGE_UI_AUTH_ADMIN_USERNAME=admin -GARAGE_UI_AUTH_ADMIN_PASSWORD=password -GARAGE_UI_AUTH_OIDC_ENABLED=false -GARAGE_UI_LOGGING_LEVEL=info ``` -**Configuration Priority:** Environment variables override YAML file settings. +## Deployment ---- +### Docker -## Project Structure - -``` -garage-ui/ -├── backend/ # Go backend (Fiber framework) -│ ├── internal/ -│ │ ├── auth/ # Authentication services (JWT, OIDC) -│ │ ├── config/ # Configuration loading -│ │ ├── handlers/ # HTTP request handlers -│ │ ├── middleware/ # CORS, auth middleware -│ │ ├── models/ # Data models and types -│ │ ├── routes/ # API route definitions -│ │ └── services/ # Business logic (S3, Garage Admin) -│ ├── pkg/ # Shared packages -│ ├── main.go # Application entry point -│ └── go.mod # Go dependencies -│ -├── frontend/ # React + TypeScript frontend -│ ├── src/ -│ │ ├── components/ # Reusable UI components -│ │ │ ├── buckets/ # Bucket-specific components -│ │ │ ├── charts/ # Data visualization -│ │ │ ├── layout/ # App layout and navigation -│ │ │ └── ui/ # shadcn/ui component library -│ │ ├── hooks/ # Custom React hooks -│ │ ├── lib/ # API client and utilities -│ │ ├── pages/ # Page components (Dashboard, Buckets, etc.) -│ │ ├── types/ # TypeScript type definitions -│ │ └── App.tsx # Main application component -│ ├── package.json # Node dependencies -│ └── vite.config.ts # Vite build configuration -│ -├── helm/ # Kubernetes Helm chart -│ └── garage-ui/ -│ ├── Chart.yaml # Chart metadata -│ ├── values.yaml # Default values -│ └── templates/ # K8s resource templates -│ -├── .github/workflows/ # CI/CD pipelines -│ ├── build.yml # Docker build and push -│ └── release.yml # Helm chart releases -│ -├── Dockerfile # Multi-stage build -├── docker-compose.yml # Local development stack -└── config.yaml.example # Configuration template +```bash +docker run -d -p 8080:8080 \ + -v $(pwd)/config.yaml:/app/config.yaml \ + noooste/garage-ui:latest ``` -**Key Directories:** +### Kubernetes -- **`backend/internal/handlers/`** - REST API endpoints for buckets, objects, users, cluster, and monitoring -- **`frontend/src/pages/`** - Main application views (Dashboard, Buckets, Cluster, Access Control) -- **`backend/internal/services/`** - Core business logic interfacing with Garage Admin API and S3 -- **`frontend/src/lib/api.ts`** - Axios-based API client with automatic error handling - ---- +```bash +helm repo add garage-ui https://helm.noste.dev/ +helm install garage-ui garage-ui/garage-ui \ + --set garage.endpoint=http://garage:3900 \ + --set garage.adminEndpoint=http://garage:3903 \ + --set garage.adminToken=your-token +``` ## Development -### Local Development Setup - -**1. Start Garage (using Docker Compose):** - -```bash -docker-compose up garage -d -``` - -**2. Run the backend:** - +Backend (Go 1.25+): ```bash cd backend go run main.go --config ../config.yaml ``` -The backend will start on `http://localhost:8080` with hot-reload (use [air](https://github.com/cosmtrek/air) for auto-reload). - -**3. Run the frontend:** - +Frontend (Node.js 25+): ```bash cd frontend npm install npm run dev ``` -The frontend dev server starts on `http://localhost:3000` with API proxy to backend. +API docs: http://localhost:8080/api/v1/ -### Available Scripts +## Garage Configuration -**Backend:** +Garage UI requires these settings in your `garage.toml`: -```bash -go run main.go # Start server -go test ./... # Run tests -swag init # Regenerate API docs -go build -o garage-ui . # Build binary +```toml +# Admin API (required for Garage UI) +[admin] +api_bind_addr = "0.0.0.0:3903" # Default: 127.0.0.1:3903 +admin_token = "your-admin-token" # Generate with: openssl rand -base64 32 + +# S3 API +[s3_api] +s3_region = "garage" # Default: "garage" +api_bind_addr = "[::]:3900" # Default: 127.0.0.1:3900 ``` -**Frontend:** +**Important:** The `admin_token` and `s3_region` in `garage.toml` must match your Garage UI `config.yaml`. -```bash -npm run dev # Development server (Vite) -npm run build # Production build -npm run lint # ESLint checks -npm run preview # Preview production build -``` - -### Testing - -```bash -# Backend tests -cd backend && go test ./internal/... - -# Frontend tests (if available) -cd frontend && npm test -``` - -### Building Docker Image - -```bash -docker build -t garage-ui:dev . -``` - -The Dockerfile uses a multi-stage build: -1. **Frontend build** - Node.js Alpine to compile React app -2. **Backend build** - Go Alpine to compile binary -3. **Runtime** - Minimal Alpine with ca-certificates - ---- - -## API Documentation - -Once the backend is running, access the Swagger UI documentation at: - -**http://localhost:8080/api/v1/** - -### API Endpoints Overview - -| Endpoint | Method | Description | -|----------|--------|-------------| -| `/health` | GET | Health check endpoint | -| `/api/v1/buckets` | GET | List all buckets | -| `/api/v1/buckets` | POST | Create a new bucket | -| `/api/v1/buckets/{name}` | GET | Get bucket details | -| `/api/v1/buckets/{name}` | DELETE | Delete a bucket | -| `/api/v1/buckets/{name}/objects` | GET | List objects in bucket | -| `/api/v1/objects/upload` | POST | Upload object to bucket | -| `/api/v1/objects/download` | GET | Download object | -| `/api/v1/objects/delete` | DELETE | Delete object | -| `/api/v1/users` | GET | List access keys | -| `/api/v1/users` | POST | Create access key | -| `/api/v1/users/{id}` | DELETE | Delete access key | -| `/api/v1/cluster/status` | GET | Cluster node status | -| `/api/v1/cluster/health` | GET | Cluster health metrics | -| `/api/v1/cluster/statistics` | GET | Storage statistics | -| `/api/v1/metrics` | GET | Prometheus metrics | - -**Authentication:** Bearer token in `Authorization` header when using OIDC or session-based auth. - ---- - -## Authentication - -Garage UI supports flexible authentication with the ability to enable one or both methods simultaneously: - -### No Authentication - -If neither admin nor OIDC authentication is enabled, the application runs without authentication - suitable for private networks or development. - -```yaml -auth: - admin: - enabled: false - oidc: - enabled: false -``` - -### Admin Authentication - -Simple username/password authentication using JWT tokens. - -```yaml -auth: - admin: - enabled: true - username: "admin" - password: "your-secure-password" - oidc: - enabled: false -``` - -### OIDC (OpenID Connect) Authentication - -Enterprise-grade authentication with providers like Keycloak, Auth0, Okta, etc. - -```yaml -auth: - admin: - enabled: false - oidc: - enabled: true - provider_name: "Keycloak" - client_id: "garage-ui" - client_secret: "your-client-secret" - issuer_url: "https://auth.example.com/realms/master" - auth_url: "https://auth.example.com/realms/master/protocol/openid-connect/auth" - token_url: "https://auth.example.com/realms/master/protocol/openid-connect/token" - userinfo_url: "https://auth.example.com/realms/master/protocol/openid-connect/userinfo" - session_max_age: 86400 # 24 hours - cookie_secure: true # Enable in production with HTTPS -``` - -### Both Authentication Methods - -You can enable both admin and OIDC authentication simultaneously. Users will be presented with both options on the login page: - -```yaml -auth: - admin: - enabled: true - username: "admin" - password: "your-secure-password" - oidc: - enabled: true - provider_name: "Keycloak" - client_id: "garage-ui" - client_secret: "your-client-secret" - issuer_url: "https://auth.example.com/realms/master" - auth_url: "https://auth.example.com/realms/master/protocol/openid-connect/auth" - token_url: "https://auth.example.com/realms/master/protocol/openid-connect/token" - userinfo_url: "https://auth.example.com/realms/master/protocol/openid-connect/userinfo" - session_max_age: 86400 - cookie_secure: true -``` - -**Role-Based Access (Optional):** - -```yaml -auth: - oidc: - role_attribute_path: "resource_access.garage-ui.roles" - admin_role: "admin" -``` - ---- - -## Deployment Best Practices - -### Production Considerations - -1. **Use HTTPS** - Always enable TLS in production: - ```yaml - auth: - oidc: - cookie_secure: true - ``` - -2. **Set Strong Admin Token** - Generate a secure token for Garage Admin API: - ```bash - openssl rand -base64 32 - ``` - -3. **Configure CORS** - Restrict allowed origins: - ```yaml - cors: - allowed_origins: - - "https://garage-ui.yourdomain.com" - ``` - -4. **Enable Monitoring** - Expose metrics endpoint for Prometheus: - ```yaml - # Metrics available at /api/v1/metrics - ``` - -5. **Resource Limits** - Set appropriate CPU/memory limits in Kubernetes/Docker - -### Health Checks - -The application provides a health check endpoint: - -```bash -curl http://localhost:8080/health -# Response: {"status":"ok","version":"0.1.0"} -``` - -Docker health check is configured automatically (every 30s). - -### Kubernetes Deployment - -The Helm chart includes: - -- **Deployment** with configurable replicas -- **Service** (ClusterIP/LoadBalancer) -- **Ingress** with TLS support -- **ConfigMap** for configuration -- **NetworkPolicy** for security -- **ServiceMonitor** for Prometheus (optional) - -Example custom values: - -```yaml -# custom-values.yaml -replicaCount: 2 - -image: - repository: noooste/garage-ui - tag: latest - -garage: - endpoint: "http://garage.storage.svc.cluster.local:3900" - adminEndpoint: "http://garage.storage.svc.cluster.local:3903" - adminToken: "your-secure-token" - -ingress: - enabled: true - className: nginx - hosts: - - host: garage-ui.example.com - paths: - - path: / - pathType: Prefix - tls: - - secretName: garage-ui-tls - hosts: - - garage-ui.example.com - -config: - auth: - admin: - enabled: false - oidc: - enabled: true - client_id: garage-ui - client_secret: secret - issuer_url: https://auth.example.com/realms/master -``` - -Install with: - -```bash -helm install garage-ui ./helm/garage-ui -f custom-values.yaml -``` - ---- - -## Technology Stack - -### Backend - -- **[Go](https://go.dev/)** 1.25+ - High-performance backend runtime -- **[Fiber v3](https://gofiber.io/)** - Express-inspired web framework -- **[Viper](https://github.com/spf13/viper)** - Configuration management -- **[Minio Go SDK](https://github.com/minio/minio-go)** - S3 client library -- **[go-oidc](https://github.com/coreos/go-oidc)** - OpenID Connect support -- **[Zerolog](https://github.com/rs/zerolog)** - Structured logging -- **[Swagger/Swag](https://github.com/swaggo/swag)** - API documentation - -### Frontend - -- **[React](https://react.dev/)** 19 - UI framework -- **[TypeScript](https://www.typescriptlang.org/)** 5.9+ - Type safety -- **[Vite](https://vitejs.dev/)** 7 - Build tool and dev server -- **[React Router](https://reactrouter.com/)** 7 - Client-side routing -- **[TanStack Query](https://tanstack.com/query)** - Server state management -- **[Zustand](https://github.com/pmndrs/zustand)** - Client state management -- **[React Hook Form](https://react-hook-form.com/)** + [Zod](https://zod.dev/) - Form validation -- **[Tailwind CSS](https://tailwindcss.com/)** 4 - Utility-first styling -- **[shadcn/ui](https://ui.shadcn.com/)** - Component library -- **[Lucide Icons](https://lucide.dev/)** - Icon library -- **[Recharts](https://recharts.org/)** - Data visualization -- **[Axios](https://axios-http.com/)** - HTTP client - ---- - -## Contributing - -Contributions are welcome! Whether it's bug reports, feature requests, or code contributions, your input helps improve Garage UI. - -### How to Contribute - -1. **Fork the repository** -2. **Create a feature branch** (`git checkout -b feature/amazing-feature`) -3. **Make your changes** -4. **Commit your changes** (`git commit -m 'Add amazing feature'`) -5. **Push to the branch** (`git push origin feature/amazing-feature`) -6. **Open a Pull Request** - -### Development Guidelines - -- Follow existing code style (use `gofmt` for Go, ESLint for TypeScript) -- Add tests for new features when possible -- Update documentation for API changes -- Keep commits atomic and descriptive -- Ensure all tests pass before submitting PR - -### Reporting Issues - -Please open an issue on GitHub with: -- Clear description of the problem -- Steps to reproduce -- Expected vs actual behavior -- Environment details (OS, Docker version, etc.) - ---- +For complete Garage configuration, see the [official documentation](https://garagehq.deuxfleurs.fr/documentation/reference-manual/configuration/). ## Troubleshooting -### Common Issues - -**Issue: "Failed to connect to Garage Admin API"** - -- **Solution:** Verify `garage.admin_endpoint` and `garage.admin_token` in config.yaml -- Check Garage Admin API is accessible: `curl http://garage:3903/status -H "Authorization: Bearer your-token"` - -**Issue: "CORS errors in browser console"** - -- **Solution:** Configure CORS in config.yaml to allow frontend origin: - ```yaml - cors: - allowed_origins: - - "http://localhost:3000" # For dev - ``` - -**Issue: "401 Unauthorized with OIDC"** - -- **Solution:** Verify OIDC configuration, especially `issuer_url`, `client_id`, and `client_secret` -- Check provider configuration allows the redirect URL: `http://your-app/auth/callback` - -**Issue: "Objects not appearing after upload"** - -- **Solution:** Check S3 endpoint connectivity and bucket permissions -- Verify Garage bucket exists: `garage bucket list` - -### Debug Mode - -Enable debug logging for troubleshooting: +**Connection failed:** +```bash +curl http://localhost:3903/status -H "Authorization: Bearer your-token" +``` +**Enable debug logs:** ```yaml logging: level: "debug" - format: "json" ``` -Or via environment variable: - -```bash -GARAGE_UI_LOGGING_LEVEL=debug -``` - ---- - ## License -This project is licensed under the **MIT License** - see the [LICENSE](LICENSE) file for details. +MIT - see [LICENSE](LICENSE) ---- +## Links -## Acknowledgments - -- [Garage](https://garagehq.deuxfleurs.fr/) - The lightweight, geo-distributed S3 storage system -- [shadcn/ui](https://ui.shadcn.com/) - Beautiful, accessible React components -- [Fiber](https://gofiber.io/) - Fast and lightweight Go web framework -- All open-source contributors who made this project possible - ---- - -## Support - -- **Documentation:** [GitHub Wiki](https://github.com/Noooste/garage-ui/wiki) (coming soon) -- **Issues:** [GitHub Issues](https://github.com/Noooste/garage-ui/issues) -- **Discussions:** [GitHub Discussions](https://github.com/Noooste/garage-ui/discussions) - ---- - -**Made with ❤️ for the Garage community** +- [Issues](https://github.com/Noooste/garage-ui/issues) +- [Contributing](CONTRIBUTING.md) +- [Garage Docs](https://garagehq.deuxfleurs.fr/documentation/)