diff --git a/nuss.go b/nuss.go index 2fff860..5cbc4ce 100644 --- a/nuss.go +++ b/nuss.go @@ -79,6 +79,51 @@ func AddUserToAnime(username string, userId, animeId int64, progress int, update return &anime, err } +func AddUserToAppointment(username string, animeId int64, meeting time.Time) (*Appointment, error) { + var appoint Appointment + err := db.Update( + func(tx *nutsdb.Tx) error { + keyBytes := Int64AndDateToBytes(animeId, meeting) + e, err := tx.Get(bucketAppoint, keyBytes) + var users []string + if err != nil { + users = make([]string, 0) + } else { + // parse user list + users, err = parseAppointmentUserList(e.Value) + if err != nil { + return err + } + } + // check if user already part + for _, u := range users { + if u == username { + // early terminate + appoint = Appointment{ + Anime: animeId, + Time: meeting, + Users: users, + } + return err + } + } + + // add user + users = append(users, username) + appoint = Appointment{ + Anime: animeId, + Time: meeting, + Users: users, + } + newData, err := json.Marshal(users) + if err != nil { + return err + } + return tx.Put(bucketAppoint, keyBytes, newData, nutsdb.Persistent) + }) + return &appoint, err +} + func DeleteUserFromAnime(username string, userId, animeId int64) (*AnimeUser, error) { var anime AnimeUser err := db.Update( @@ -167,6 +212,50 @@ func DeleteUserFromAnimes(userId int64) error { }) } +func DeleteUserFromAppointment(username string, animeId int64, meeting time.Time) (*Appointment, error) { + var appoint Appointment + err := db.Update( + func(tx *nutsdb.Tx) error { + keyBytes := Int64AndDateToBytes(animeId, meeting) + e, err := tx.Get(bucketAppoint, keyBytes) + var users []string + if err != nil { + users = make([]string, 0) + } else { + // parse user list + users, err = parseAppointmentUserList(e.Value) + if err != nil { + return err + } + } + // check if user already part + for i, u := range users { + // early terminate + if u == username { + // delete user from list + users[i] = users[len(users)-1] + users = users[:len(users)-1] + // check if appointment needs recycling + appoint = Appointment{ + Anime: animeId, + Time: meeting, + Users: users, + } + if len(users) == 0 { + return tx.Delete(bucketAppoint, keyBytes) + } + newData, err := json.Marshal(users) + if err != nil { + return err + } + return tx.Put(bucketAppoint, keyBytes, newData, nutsdb.Persistent) + } + } + return fmt.Errorf("%s schaut nicht %d um %s", username, animeId, meeting.Format(time.RFC3339)) + }) + return &appoint, err +} + func DeleteAnime(tx *nutsdb.Tx, keyBytes []byte) error { tx.Delete(bucketChat, keyBytes) return tx.Delete(bucketAnime, keyBytes) diff --git a/ober.go b/ober.go index c4f4b7e..ca5156a 100644 --- a/ober.go +++ b/ober.go @@ -6,6 +6,7 @@ import ( "log" "strconv" "strings" + "time" "github.com/fasthttp/router" "github.com/valyala/fasthttp" @@ -26,9 +27,11 @@ func RunWebserv() { r.GET("/api/user/{user?}", Headers(UserGet)) r.GET("/api/watch/{user?}", Headers(WatchGet)) r.GET("/api/watchext/{user?}", Headers(WatchExtendedGet)) + r.POST("/api/appointment/{user}", Headers(AppointmentPost)) r.POST("/api/chat/{id}/{user}", Headers(ChatPost)) r.POST("/api/register", Headers(Register)) r.POST("/api/watch/{user}", Headers(WatchPost)) + r.DELETE("/api/appointment/{user}", Headers(AppointmentDelete)) r.DELETE("/api/register", Headers(UnRegister)) r.DELETE("/api/watch/{user}", Headers(WatchDelete)) log.Fatal(fasthttp.ListenAndServe(":"+strconv.Itoa(*webServerPort), r.Handler)) @@ -395,6 +398,10 @@ func Register(ctx *fasthttp.RequestCtx) { ctx.SetStatusCode(fasthttp.StatusOK) } +func AppointmentPost(ctx *fasthttp.RequestCtx) { + processUpdateAppointmentReq(ctx, true) +} + func ChatPost(ctx *fasthttp.RequestCtx) { auth := ctx.Request.Header.Peek("X-HUSO-AUTH") if ctx.UserValue("id") == nil || ctx.UserValue("user") == nil || auth == nil || string(auth) == "" || !strings.Contains(string(ctx.Request.Header.ContentType()), "text/plain") { @@ -445,6 +452,10 @@ func WatchPost(ctx *fasthttp.RequestCtx) { processUpdateReq(ctx, true) } +func AppointmentDelete(ctx *fasthttp.RequestCtx) { + processUpdateAppointmentReq(ctx, false) +} + func UnRegister(ctx *fasthttp.RequestCtx) { if !strings.Contains(string(ctx.Request.Header.ContentType()), "application/json") { ctx.SetStatusCode(fasthttp.StatusBadRequest) @@ -597,9 +608,15 @@ func processUpdateReq(ctx *fasthttp.RequestCtx, update bool) { ctx.SetStatusCode(fasthttp.StatusConflict) return } + // anime is already dropped big baka user + if listState == malApiStatusD { + ctx.WriteString("Du hast schon gedropped bro") + ctx.SetStatusCode(fasthttp.StatusConflict) + return + } animeData, err = AddUserToAnime(username, userId, anime.Anime, progress, updated) } else { - // anime exitsts => delete + // anime exists => delete animeData, err = DeleteUserFromAnime(username, userId, anime.Anime) } if err != nil { @@ -622,6 +639,88 @@ func processUpdateReq(ctx *fasthttp.RequestCtx, update bool) { ctx.SetContentType("application/json; charset=utf-8") } +func processUpdateAppointmentReq(ctx *fasthttp.RequestCtx, update bool) { + auth := ctx.Request.Header.Peek("X-HUSO-AUTH") + if ctx.UserValue("user") == nil || auth == nil || string(auth) == "" || !strings.Contains(string(ctx.Request.Header.ContentType()), "application/json") { + ctx.SetStatusCode(fasthttp.StatusBadRequest) + return + } + username := fmt.Sprintf("%s", ctx.UserValue("user")) + legit, userId := GheddoAuth(username, string(auth)) + if !legit { + ctx.SetStatusCode(fasthttp.StatusUnauthorized) + return + } + + body := ctx.PostBody() + var appoints []Appointment + err := json.Unmarshal(body, &appoints) + if err != nil || len(appoints) == 0 { + ctx.WriteString(err.Error()) + ctx.SetStatusCode(fasthttp.StatusBadRequest) + return + } + + // iterate sent appointments + for i, appointment := range appoints { + var appData *Appointment + var found bool + + if update { + // kann sich (noch) nicht in der Vergagenheit verabreden + if appointment.Time.Before(time.Now()) { + ctx.WriteString("kann sich (noch) nicht in der Vergagenheit verabreden") + ctx.SetStatusCode(fasthttp.StatusBadRequest) + return + } + // check if user is a troll + found, err = CheckAnimeExistInDbAndUserWatches(appointment.Anime, userId) + if err != nil { + addErrorToCtx(ctx, err) + return + } + if !found { + ctx.WriteString("Anime gibts net oder du schaust net") + ctx.SetStatusCode(fasthttp.StatusNotFound) + return + } + + // save appointment and get list + appData, err = AddUserToAppointment(username, appointment.Anime, appointment.Time) + } else { + found, err = CheckAnimeExistInDb(appointment.Anime) + if err != nil { + addErrorToCtx(ctx, err) + return + } + if !found { + ctx.WriteString("Anime gibts net") + ctx.SetStatusCode(fasthttp.StatusNotFound) + return + } + // anime exists => delete + appData, err = DeleteUserFromAppointment(username, appointment.Anime, appointment.Time) + } + if err != nil { + addErrorToCtx(ctx, err) + return + } + appoints[i].Users = appData.Users + } + + data, err := json.Marshal(appoints) + if err != nil { + addErrorToCtx(ctx, err) + return + } + err = writeResponseBody(ctx, data) + if err != nil { + addErrorToCtx(ctx, err) + return + } + ctx.SetContentType("application/json; charset=utf-8") +} + func writeResponseBody(ctx *fasthttp.RequestCtx, bytes []byte) error { _, err := ctx.Write(bytes) if err != nil { diff --git a/schaffer.go b/schaffer.go index c3f35e9..6c00830 100644 --- a/schaffer.go +++ b/schaffer.go @@ -232,3 +232,21 @@ func CheckAnimeExistInDb(animeId int64) (bool, error) { } return false, err } + +func CheckAnimeExistInDbAndUserWatches(animeId, userId int64) (bool, error) { + dbAnime, err := ReadAnimeUsers() + if err != nil { + return false, err + } + for _, a := range dbAnime { + if a.Anime == animeId { + for _, u := range a.Users { + if u.MalID == userId { + return true, err + } + } + return false, err + } + } + return false, err +}