A lightweight object storage and static site hosting MVP with a Go API, React console, MySQL metadata, and local filesystem object storage.
English | Chinese
light-oss is designed for internal tools, prototypes, lightweight private deployments, and systems that need a simple object storage base that is easy to run and modify.
It is not a full S3-compatible service. The current implementation focuses on a clear MVP: bucket management, object upload/download, directory browsing, signed downloads, static site hosting, and a web console.
backend/: Go + Gin API for buckets, objects, folders, signed URLs, and site bindings.frontend/: React + TypeScript + Vite management console.gateway/: Nginx gateway for console, API, and hosted-site routing.docker-compose.yml: local stack for MySQL, backend, frontend, and gateway.
- Bucket create/list/delete, including cascading object and site cleanup.
- Object upload, download, metadata lookup, listing, deletion, and
public/privatevisibility. - Bearer Token authentication for private APIs and private object access.
- Signed download URLs for private objects.
- Folder tree, folder creation/deletion, directory entries, search, pagination, and ZIP folder download.
- Batch folder upload with
multipart/form-dataand a manifest. - Static site hosting from
bucket + root_prefix, with custom domains, index document, error document, and SPA fallback. - Health checks, upload size limits, basic rate limiting,
request_id, and structured error responses.
- Backend: Go 1.22+, Gin, GORM, golang-migrate
- Frontend: React, TypeScript, Vite, TanStack Query, Axios
- UI: Tailwind CSS, Radix / shadcn-style components
- Database: MySQL 8.x
- Gateway: Nginx
.
├─ backend/
│ ├─ cmd/server
│ ├─ docs/openapi.apifox.json
│ ├─ internal/
│ └─ migrations/
├─ frontend/
├─ gateway/
├─ docker-compose.yml
├─ Makefile
└─ .env
- Go 1.22+
- Node.js 20+
- npm
- MySQL 8.x
- Docker Desktop or Docker Engine, if you use Compose
The simplest local setup is to run MySQL with Docker, then run the backend and frontend directly on your machine.
-
Prepare the root
.envor.env.personal.Local backend and frontend commands prefer
.env.personalwhen it exists. If.env.personalis found,.envis not merged for missing values, so keep.env.personalcomplete.Recommended local values:
APP_ENV=development APP_ADDR=:8080 APP_PUBLIC_BASE_URL=http://localhost:8080 APP_STORAGE_ROOT=./light-oss-data/storage APP_BEARER_TOKENS=light-oss APP_SIGNING_SECRET=change-me-in-local-dev DB_DSN=root:112233ss@tcp(localhost:3306)/light-oss?charset=utf8mb4&parseTime=True&loc=UTC&multiStatements=true VITE_DEFAULT_API_BASE_URL=http://localhost:8080 VITE_DEFAULT_BEARER_TOKEN=light-oss
-
Start MySQL.
docker compose up -d mysql
-
Start the backend.
cd backend go test ./... go run ./cmd/server
The API listens on
http://localhost:8080by default. -
Start the frontend in another terminal.
cd frontend npm install npm test npm run dev
The console runs at
http://localhost:3000by default. -
Open
/settingsin the console and confirm the API Base URL and Bearer Token.
Use this mode when you want to verify the full gateway and hosted-site domain flow.
-
Adjust the root
.envfor gateway mode.APP_PUBLIC_BASE_URL=http://api.localhost APP_STORAGE_ROOT=/data/storage VITE_DEFAULT_API_BASE_URL=http://api.localhost VITE_DEFAULT_BEARER_TOKEN=light-oss
-
Add local hosts entries if needed.
127.0.0.1 console.localhost 127.0.0.1 api.localhost 127.0.0.1 demo.localhost -
Start the stack.
docker compose up --build
Or:
make up
-
Visit the services.
- Console:
http://console.localhost - API:
http://api.localhost - MySQL:
localhost:3306 - Example hosted site:
http://demo.localhost
- Console:
Gateway routing:
console.localhost-> frontendapi.localhost-> backend API- other hostnames -> backend static site resolver
Custom site domains must point to the gateway through DNS or hosts. The gateway currently handles HTTP only; HTTPS, certificate management, and production DNS automation are outside this MVP.
- Root
.envis used by Docker Compose. - Root
.env.personalis preferred by local backend/frontend commands when it exists. - Frontend settings saved in browser
localStorageoverrideVITE_DEFAULT_API_BASE_URLandVITE_DEFAULT_BEARER_TOKEN. APP_PUBLIC_BASE_URLcontrols generated signed download URLs.APP_STORAGE_ROOTshould be a local path for direct local runs and/data/storagein Compose.APP_BEARER_TOKENSis the Bearer Token allowlist. Multiple tokens are comma-separated.- Do not commit real production passwords, signing secrets, tokens, or domain configuration.
- OpenAPI document:
backend/docs/openapi.apifox.json - Main authenticated API prefix:
/api/v1 - Public health check:
GET /healthz - Authenticated health check:
GET /api/v1/healthz - Object API path keys are full object paths, so nested
/segments act as directory-like prefixes. - Static sites only serve
publicobjects. - Successful JSON responses usually use
{"request_id":"...","data":...}. - Failed JSON responses usually use
{"request_id":"...","error":{"code":"...","message":"..."}}.
For manual API testing, set:
BASE_URL=http://localhost:8080
TOKEN=light-ossThen call authenticated endpoints with:
curl "$BASE_URL/api/v1/buckets" \
-H "Authorization: Bearer $TOKEN"make test
make lint
cd backend && go test ./...
cd frontend && npm test
cd frontend && npm run lint
cd frontend && npm run buildUseful logs in Compose mode:
docker compose logs -f mysql
docker compose logs -f backend
docker compose logs -f frontend
docker compose logs -f gateway- Object content is stored on the local filesystem, not in distributed storage.
- Static site hosting only serves public objects.
- Gateway mode currently supports HTTP only.
- The project is a lightweight OSS MVP, not a full S3-compatible implementation.