Initial commit: wb-mock skeleton
This commit is contained in:
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
wb-mock
|
||||
*.log
|
||||
.DS_Store
|
||||
60
AGENTS.md
Normal file
60
AGENTS.md
Normal 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
11
Dockerfile
Normal 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
25
Makefile
Normal 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
38
cmd/server/main.go
Normal 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))
|
||||
}
|
||||
22
internal/config/config.go
Normal file
22
internal/config/config.go
Normal 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
|
||||
}
|
||||
22
internal/handlers/handlers.go
Normal file
22
internal/handlers/handlers.go
Normal 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)
|
||||
}
|
||||
33
internal/middleware/auth.go
Normal file
33
internal/middleware/auth.go
Normal 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,
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user