matrix-veles/internal/web/api/bot_entries.go

230 lines
6 KiB
Go

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)
}