Initial commit: wb-mock skeleton

This commit is contained in:
root
2026-07-23 19:02:03 +03:00
commit 93ebcf4406
9 changed files with 217 additions and 0 deletions

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
wb-mock
*.log
.DS_Store

60
AGENTS.md Normal file
View File

@ -0,0 +1,60 @@
# AGENTS.md — WB Mock Server — Project Context
> **Read this first.** Append what you did at the end under **Session Log**.
## 1. What this is
Mock server emulating Wildberries Seller API for integration testing. Returns hardcoded fixture data.
| Layer | Tech |
|-------|------|
| Language | Go 1.25, `net/http` stdlib |
| Deployment | Docker + systemd on ioffe |
| Auth | Optional Api-Key validation (WB Authorization header) |
| Config | Env vars: `PORT`, `WB_API_KEY` |
## 2. Endpoints (TBD by agent)
| Method | Path | Description |
|--------|------|-------------|
| GET | `/health` | Health check |
## 3. Test Data
| Entity | Count |
|--------|-------|
| Products | TBD |
| Stocks | TBD |
| Prices | TBD |
## 4. Deploy
```bash
cd /Users/rinat/projects/wb-mock
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o wb-mock ./cmd/server
scp wb-mock ioffe:/opt/wb-mock/wb-mock
ssh ioffe "sudo systemctl restart wb-mock"
curl https://wb-mock.px.kinrin.ru/health # expect {"status":"ok"}
```
## 5. Key Commands
```bash
make build # binary
make test # go test -race ./...
make docker # Docker build
go run ./cmd/server # local on :8080
```
## 6. Wildberries API Auth
WB Seller API uses `Authorization: <api_key>` header (no Client-Id).
## 7. Reference
Ozon mock sibling project: `/Users/rinat/projects/ozon-mock` — same architecture, 20 endpoints, 31 tests.
Use it as a reference for patterns: config loading, fixture generation, handler structure, filter logic.
## 8. Session Log
> Append your summary below. Latest first.

11
Dockerfile Normal file
View File

@ -0,0 +1,11 @@
FROM alpine:3.20
RUN apk --no-cache add ca-certificates tzdata
COPY wb-mock /usr/local/bin/wb-mock
EXPOSE 8080
ENV PORT=8080
CMD ["/usr/local/bin/wb-mock"]

25
Makefile Normal file
View File

@ -0,0 +1,25 @@
.PHONY: build test docker run clean vet lint
build:
CGO_ENABLED=0 go build -o wb-mock ./cmd/server
test:
go test -v -race ./...
vet:
go vet ./...
lint:
go vet ./...
run:
go run ./cmd/server
docker:
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o wb-mock ./cmd/server
docker build -t wb-mock .
clean:
rm -f wb-mock
all: vet test build

38
cmd/server/main.go Normal file
View File

@ -0,0 +1,38 @@
package main
import (
"log"
"net/http"
"github.com/rinat/wb-mock/internal/config"
"github.com/rinat/wb-mock/internal/middleware"
)
func main() {
cfg := config.Load()
mux := http.NewServeMux()
auth := func(h http.HandlerFunc) http.HandlerFunc {
return middleware.Auth(cfg.APIKey, h)
}
// Health
mux.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.Write([]byte(`{"status":"ok"}`))
})
_ = auth
// 404 handler
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusNotFound)
w.Write([]byte(`{"code":404,"message":"not found"}`))
})
addr := ":" + cfg.Port
log.Printf("Starting wb-mock on %s", addr)
log.Fatal(http.ListenAndServe(addr, mux))
}

3
go.mod Normal file
View File

@ -0,0 +1,3 @@
module github.com/rinat/wb-mock
go 1.25.0

22
internal/config/config.go Normal file
View File

@ -0,0 +1,22 @@
package config
import "os"
type Config struct {
Port string
APIKey string
}
func Load() *Config {
return &Config{
Port: getEnv("PORT", "8080"),
APIKey: os.Getenv("WB_API_KEY"),
}
}
func getEnv(key, defaultVal string) string {
if val := os.Getenv(key); val != "" {
return val
}
return defaultVal
}

View File

@ -0,0 +1,22 @@
package handlers
import (
"encoding/json"
"log"
"net/http"
)
func writeJSON(w http.ResponseWriter, status int, v any) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(status)
json.NewEncoder(w).Encode(v)
}
func decodeBody(r *http.Request, v any) error {
defer r.Body.Close()
return json.NewDecoder(r.Body).Decode(v)
}
func logRequest(r *http.Request) {
log.Printf("[wb-mock] %s %s", r.Method, r.URL.Path)
}

View File

@ -0,0 +1,33 @@
package middleware
import (
"encoding/json"
"net/http"
)
func Auth(expectedAPIKey string, next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
apiKey := r.Header.Get("Authorization")
if apiKey == "" {
writeError(w, 401, "missing credentials: Authorization header is required")
return
}
if expectedAPIKey != "" && apiKey != expectedAPIKey {
writeError(w, 403, "invalid api key")
return
}
next(w, r)
}
}
func writeError(w http.ResponseWriter, code int, msg string) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(code)
json.NewEncoder(w).Encode(map[string]any{
"code": code,
"message": msg,
})
}