package api import ( "encoding/json" "errors" "fmt" "github.com/Unkn0wnCat/matrix-veles/internal/db" "github.com/Unkn0wnCat/matrix-veles/internal/db/model" "github.com/go-chi/chi/v5" "go.mongodb.org/mongo-driver/bson/primitive" "go.mongodb.org/mongo-driver/mongo" "log" "net/http" "net/url" "strconv" "time" ) type apiEntryPostBody struct { Hash string `json:"hash"` Tags []string `json:"tags"` PartOf []*primitive.ObjectID `json:"part_of"` FileURL string `json:"file_url"` Comment *string `json:"comment"` } func apiHandleBotEntriesPost(res http.ResponseWriter, req *http.Request) { var body apiEntryPostBody err := json.NewDecoder(req.Body).Decode(&body) if err != nil { writeJSONError(res, http.StatusBadRequest, errors.New("malformed body")) return } existingEntry, err := db.GetEntryByHash(body.Hash) if err == nil { writeJSONError(res, http.StatusConflict, fmt.Errorf("hash already in database: %s", existingEntry.ID)) return } if !errors.Is(err, mongo.ErrNoDocuments) { // TODO: LOG THIS ERROR writeJSONError(res, http.StatusInternalServerError, errors.New("database error")) return } user := getClaims(req) userId, err := primitive.ObjectIDFromHex(user.Subject) if err != nil { // TODO: LOG THIS ERROR log.Println(userId) writeJSONError(res, http.StatusInternalServerError, errors.New("internal corruption 0x01")) return } for i, partOf := range body.PartOf { list, err := db.GetListByID(*partOf) if err != nil { if errors.Is(err, mongo.ErrNoDocuments) { writeJSONError(res, http.StatusNotFound, fmt.Errorf("invalid partof value at index %d - not found", i)) return } // TODO: LOG THIS ERROR writeJSONError(res, http.StatusInternalServerError, errors.New("database error")) return } authorized := false for _, maintainer := range list.Maintainers { log.Println(maintainer, userId) if maintainer.Hex() == userId.Hex() { authorized = true break } } if !authorized { writeJSONError(res, http.StatusUnauthorized, fmt.Errorf("invalid partof value at index %d - not authorized", i)) return } } newEntry := model.DBEntry{ ID: primitive.NewObjectID(), Tags: body.Tags, PartOf: body.PartOf, HashValue: body.Hash, FileURL: body.FileURL, Timestamp: time.Now(), AddedBy: &userId, Comments: nil, } if body.Comment != nil && *body.Comment != "" { newEntry.Comments = append(newEntry.Comments, &model.DBComment{ CommentedBy: &userId, Content: *body.Comment, Timestamp: time.Now(), }) } err = db.SaveEntry(&newEntry) if err != nil { // TODO: LOG THIS ERROR writeJSONError(res, http.StatusInternalServerError, errors.New("database error")) return } encoded, err := json.Marshal(newEntry) if err != nil { // TODO: LOG THIS ERROR writeJSONError(res, http.StatusInternalServerError, errors.New("could not marshal data")) return } res.Header().Set("Content-Type", "application/json") res.WriteHeader(http.StatusOK) res.Write(encoded) } func apiHandleBotEntriesList(res http.ResponseWriter, req *http.Request) { requestUri, err := url.ParseRequestURI(req.RequestURI) if err != nil { writeJSONError(res, http.StatusBadRequest, errors.New("unable to parse uri")) return } first := int64(50) var cursor *primitive.ObjectID if requestUri.Query().Has("first") { first2, err := strconv.Atoi(requestUri.Query().Get("first")) if err != nil { writeJSONError(res, http.StatusBadRequest, errors.New("malformed query")) return } first = int64(first2) } if requestUri.Query().Has("cursor") { cursor2, err := primitive.ObjectIDFromHex(requestUri.Query().Get("cursor")) if err != nil { writeJSONError(res, http.StatusBadRequest, errors.New("malformed query")) return } cursor = &cursor2 } entries, err := db.GetEntries(first, cursor) if err != nil { if !errors.Is(err, mongo.ErrNoDocuments) { // TODO: LOG THIS ERROR writeJSONError(res, http.StatusInternalServerError, errors.New("database error")) return } res.Header().Set("Content-Type", "application/json") res.WriteHeader(http.StatusOK) res.Write([]byte("[]")) return } encoded, err := json.Marshal(entries) if err != nil { // TODO: LOG THIS ERROR writeJSONError(res, http.StatusInternalServerError, errors.New("could not marshal data")) return } res.Header().Set("Content-Type", "application/json") res.WriteHeader(http.StatusOK) res.Write(encoded) } func apiHandleBotEntry(res http.ResponseWriter, req *http.Request) { requestedId := chi.URLParam(req, "id") objectId, err := primitive.ObjectIDFromHex(requestedId) if err != nil { writeJSONError(res, http.StatusNotFound, errors.New("malformed id")) return } entry, err := db.GetEntryByID(objectId) if err != nil { if errors.Is(err, mongo.ErrNoDocuments) { writeJSONError(res, http.StatusNotFound, errors.New("not found")) return } // TODO: LOG THIS ERROR writeJSONError(res, http.StatusInternalServerError, errors.New("database error")) return } encoded, err := json.Marshal(entry) if err != nil { // TODO: LOG THIS ERROR writeJSONError(res, http.StatusInternalServerError, errors.New("could not marshal data")) return } res.Header().Set("Content-Type", "application/json") res.WriteHeader(http.StatusOK) res.Write(encoded) } func apiHandleBotEntryByHash(res http.ResponseWriter, req *http.Request) { requestedHash := chi.URLParam(req, "hash") entry, err := db.GetEntryByHash(requestedHash) if err != nil { if errors.Is(err, mongo.ErrNoDocuments) { writeJSONError(res, http.StatusNotFound, errors.New("not found")) return } // TODO: LOG THIS ERROR writeJSONError(res, http.StatusInternalServerError, errors.New("database error")) return } encoded, err := json.Marshal(entry) if err != nil { // TODO: LOG THIS ERROR writeJSONError(res, http.StatusInternalServerError, errors.New("could not marshal data")) return } res.Header().Set("Content-Type", "application/json") res.WriteHeader(http.StatusOK) res.Write(encoded) }