From 7c1ceb8adccfa61e4554ea4a4c2aba65d3de3961 Mon Sep 17 00:00:00 2001 From: Dmitrii Vasianin Date: Sun, 5 Oct 2025 15:37:59 +0300 Subject: [PATCH 1/3] [software engineering] add some api endpoints --- go.mod | 2 + software_engineering/main.go | 107 ++++++++++++++++++++++++++++-- software_engineering/main_test.go | 58 +++++++++++++--- 3 files changed, 152 insertions(+), 15 deletions(-) diff --git a/go.mod b/go.mod index 8f255a0..0c96c64 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,5 @@ module interactive-whiteboard-docs go 1.23.9 + +require github.com/gorilla/mux v1.8.1 diff --git a/software_engineering/main.go b/software_engineering/main.go index 248f092..1d44a03 100644 --- a/software_engineering/main.go +++ b/software_engineering/main.go @@ -1,17 +1,110 @@ package main import ( - "fmt" + "encoding/json" + "log" "net/http" + "sync" + + "github.com/gorilla/mux" ) -func main() { - http.HandleFunc("/ping", PingHandler) - if err := http.ListenAndServe(":8080", nil); err != nil { - panic(err) +type Board struct { + UID string `json:"uid"` + Title string `json:"title"` +} + +var ( + boards = make(map[string]Board) + mu sync.Mutex + apiKey = "secret123" +) + +func auth(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.Header.Get("X-API-Key") != apiKey { + http.Error(w, "unauthorized", http.StatusUnauthorized) + return + } + next.ServeHTTP(w, r) + }) +} + +// POST /board — создать доску +func createBoard(w http.ResponseWriter, r *http.Request) { + var b Board + if err := json.NewDecoder(r.Body).Decode(&b); err != nil { + http.Error(w, "bad request", http.StatusBadRequest) + return + } + + if b.UID == "" || b.Title == "" { + http.Error(w, "bad request", http.StatusBadRequest) + return + } + + mu.Lock() + boards[b.UID] = b + mu.Unlock() + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusCreated) + _ = json.NewEncoder(w).Encode(b) +} + +// GET /board/{uid} — получить инфу о доске +func getBoard(w http.ResponseWriter, r *http.Request) { + uid := mux.Vars(r)["uid"] + + if uid == "" { + http.Error(w, "bad request", http.StatusBadRequest) + return } + + mu.Lock() + b, ok := boards[uid] + mu.Unlock() + + if !ok { + http.Error(w, "not found", http.StatusNotFound) + return + } + + w.Header().Set("Content-Type", "application/json") + _ = json.NewEncoder(w).Encode(b) +} + +// DELETE /board/{uid} — удалить доску +func deleteBoard(w http.ResponseWriter, r *http.Request) { + uid := mux.Vars(r)["uid"] + + if uid == "" { + http.Error(w, "bad request", http.StatusBadRequest) + return + } + + mu.Lock() + _, ok := boards[uid] + if ok { + delete(boards, uid) + } + mu.Unlock() + + if !ok { + http.Error(w, "not found", http.StatusNotFound) + return + } + + w.WriteHeader(http.StatusNoContent) } -func PingHandler(w http.ResponseWriter, r *http.Request) { - _, _ = fmt.Fprint(w, "pong\n") +func main() { + r := mux.NewRouter() + + r.Handle("/board", auth(http.HandlerFunc(createBoard))).Methods(http.MethodPost) + r.Handle("/board/{uid}", auth(http.HandlerFunc(getBoard))).Methods(http.MethodGet) + r.Handle("/board/{uid}", auth(http.HandlerFunc(deleteBoard))).Methods(http.MethodDelete) + + log.Println("server running at :8080") + log.Fatal(http.ListenAndServe(":8080", r)) } diff --git a/software_engineering/main_test.go b/software_engineering/main_test.go index e4df875..79c570d 100644 --- a/software_engineering/main_test.go +++ b/software_engineering/main_test.go @@ -1,22 +1,64 @@ package main import ( - "io/ioutil" + "bytes" + "encoding/json" "net/http" "net/http/httptest" "testing" + + "github.com/gorilla/mux" ) -func TestPingHandler(t *testing.T) { - req := httptest.NewRequest(http.MethodGet, "/ping", nil) +func setupRouter() *mux.Router { + r := mux.NewRouter() + r.Handle("/board", auth(http.HandlerFunc(createBoard))).Methods(http.MethodPost) + r.Handle("/board/{uid}", auth(http.HandlerFunc(getBoard))).Methods(http.MethodGet) + r.Handle("/board/{uid}", auth(http.HandlerFunc(deleteBoard))).Methods(http.MethodDelete) + return r +} + +func newReq(method, url string, body []byte) *http.Request { + req := httptest.NewRequest(method, url, bytes.NewReader(body)) + req.Header.Set("X-API-Key", apiKey) + return req +} + +func TestBoardAPI(t *testing.T) { + boards = make(map[string]Board) + router := setupRouter() + + // Создать доску + body := []byte(`{"uid":"b1","title":"Первая доска"}`) w := httptest.NewRecorder() + router.ServeHTTP(w, newReq(http.MethodPost, "/board", body)) + if w.Code != http.StatusCreated { + t.Fatalf("ожидали 201, получили %d", w.Code) + } - PingHandler(w, req) + // Получить доску + w = httptest.NewRecorder() + router.ServeHTTP(w, newReq(http.MethodGet, "/board/b1", nil)) + if w.Code != http.StatusOK { + t.Fatalf("ожидали 200, получили %d", w.Code) + } + var b Board + _ = json.NewDecoder(w.Body).Decode(&b) + if b.Title != "Первая доска" { + t.Errorf("ожидали title=Первая доска, получили %s", b.Title) + } - resp := w.Result() - body, _ := ioutil.ReadAll(resp.Body) + // Удалить доску + w = httptest.NewRecorder() + router.ServeHTTP(w, newReq(http.MethodDelete, "/board/b1", nil)) + if w.Code != http.StatusNoContent { + t.Fatalf("ожидали 204, получили %d", w.Code) + } - if string(body) != "pong\n" { - t.Errorf("ожидали pong, получили %s", string(body)) + // Повторное удаление -> 404 + w = httptest.NewRecorder() + router.ServeHTTP(w, newReq(http.MethodDelete, "/board/b1", nil)) + if w.Code != http.StatusNotFound { + t.Fatalf("ожидали 404, получили %d", w.Code) } } From 10795726d1ff2b0425a700c18337df81d6649225 Mon Sep 17 00:00:00 2001 From: Dmitrii Vasianin Date: Sun, 5 Oct 2025 15:42:13 +0300 Subject: [PATCH 2/3] [software engineering] fix docker --- Dockerfile | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/Dockerfile b/Dockerfile index 5af5f10..21d21bd 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,10 +1,18 @@ FROM golang:1.23 AS builder + WORKDIR /app -COPY . . -RUN cd software_engineering && go build -o server main.go -FROM debian:stable-slim -WORKDIR /root/ -COPY --from=builder /app/software_engineering/server . +COPY software_engineering/go.mod software_engineering/go.sum ./ +RUN go mod download + +COPY software_engineering/ ./ + +RUN CGO_ENABLED=0 GOOS=linux go build -o server main.go + +FROM scratch + +WORKDIR /root +COPY --from=builder /app/server . + EXPOSE 8080 CMD ["./server"] From 7c08e7b5929a63c7b5b430386bd3dc3f0f8782ea Mon Sep 17 00:00:00 2001 From: Dmitrii Vasianin Date: Sun, 5 Oct 2025 15:44:01 +0300 Subject: [PATCH 3/3] [software engineering] fix docker --- Dockerfile | 9 +++++---- go.sum | 2 ++ 2 files changed, 7 insertions(+), 4 deletions(-) create mode 100644 go.sum diff --git a/Dockerfile b/Dockerfile index 21d21bd..6147050 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,17 +2,18 @@ FROM golang:1.23 AS builder WORKDIR /app -COPY software_engineering/go.mod software_engineering/go.sum ./ +COPY go.mod go.sum ./ RUN go mod download -COPY software_engineering/ ./ +COPY software_engineering/ ./software_engineering/ -RUN CGO_ENABLED=0 GOOS=linux go build -o server main.go +WORKDIR /app/software_engineering +RUN CGO_ENABLED=0 GOOS=linux go build -o /server main.go FROM scratch WORKDIR /root -COPY --from=builder /app/server . +COPY --from=builder /server . EXPOSE 8080 CMD ["./server"] diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..7128337 --- /dev/null +++ b/go.sum @@ -0,0 +1,2 @@ +github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= +github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=