start reports/moderation in backend

This commit is contained in:
Sam 2023-03-19 16:14:09 +01:00
parent 41edaee8ea
commit 33f903b07d
No known key found for this signature in database
GPG key ID: B4EF20DDE721CAA1
8 changed files with 136 additions and 2 deletions

47
backend/db/report.go Normal file
View file

@ -0,0 +1,47 @@
package db
import (
"context"
"time"
"emperror.dev/errors"
"github.com/georgysavva/scany/pgxscan"
"github.com/rs/xid"
)
type Report struct {
ID int64
UserID xid.ID
MemberID *xid.ID
Reason string
ReporterID xid.ID
CreatedAt time.Time
ResolvedAt *time.Time
AdminID *xid.ID
AdminComment *string
}
const reportPageSize = 100
func (db *DB) Reports(ctx context.Context, closed bool, page int) (rs []Report, err error) {
builder := sq.Select("*").From("reports").Offset(uint64(reportPageSize * page)).Limit(reportPageSize).OrderBy("id ASC")
if closed {
builder = builder.Where("resolved_at IS NOT NULL")
} else {
builder = builder.Where("resolved_at IS NULL")
}
sql, args, err := builder.ToSql()
if err != nil {
return nil, errors.Wrap(err, "building sql")
}
err = pgxscan.Select(ctx, db, &rs, sql, args...)
if err != nil {
return nil, errors.Wrap(err, "executing query")
}
if len(rs) == 0 {
return []Report{}, nil
}
return rs, nil
}

View file

@ -34,6 +34,7 @@ type User struct {
FediverseInstance *string
MaxInvites int
IsAdmin bool
DeletedAt *time.Time
SelfDelete *bool

View file

@ -5,6 +5,7 @@ import (
"codeberg.org/u1f320/pronouns.cc/backend/routes/bot"
"codeberg.org/u1f320/pronouns.cc/backend/routes/member"
"codeberg.org/u1f320/pronouns.cc/backend/routes/meta"
"codeberg.org/u1f320/pronouns.cc/backend/routes/mod"
"codeberg.org/u1f320/pronouns.cc/backend/routes/user"
"codeberg.org/u1f320/pronouns.cc/backend/server"
"github.com/go-chi/chi/v5"
@ -20,5 +21,6 @@ func mountRoutes(s *server.Server) {
member.Mount(s, r)
bot.Mount(s, r)
meta.Mount(s, r)
mod.Mount(s, r)
})
}

View file

@ -107,7 +107,7 @@ func (s *Server) discordCallback(w http.ResponseWriter, r *http.Request) error {
// TODO: implement user + token permissions
tokenID := xid.New()
token, err := s.Auth.CreateToken(u.ID, tokenID, false, false, true)
token, err := s.Auth.CreateToken(u.ID, tokenID, u.IsAdmin, false, true)
if err != nil {
return err
}

View file

@ -128,7 +128,7 @@ func (s *Server) mastodonCallback(w http.ResponseWriter, r *http.Request) error
// TODO: implement user + token permissions
tokenID := xid.New()
token, err := s.Auth.CreateToken(u.ID, tokenID, false, false, true)
token, err := s.Auth.CreateToken(u.ID, tokenID, u.IsAdmin, false, true)
if err != nil {
return err
}

View file

@ -0,0 +1,13 @@
package mod
import (
"fmt"
"net/http"
)
func (s *Server) getReports(w http.ResponseWriter, r *http.Request) error {
showClosed := r.FormValue("closed") == "true"
fmt.Println("closed =", showClosed)
return nil
}

View file

@ -0,0 +1,52 @@
package mod
import (
"net/http"
"codeberg.org/u1f320/pronouns.cc/backend/server"
"github.com/go-chi/chi/v5"
"github.com/go-chi/render"
)
type Server struct {
*server.Server
}
func Mount(srv *server.Server, r chi.Router) {
s := &Server{Server: srv}
r.With(MustAdmin).Route("/admin", func(r chi.Router) {
r.Get("/reports", server.WrapHandler(s.getReports))
r.Get("/reports/by-user/{id}", nil)
r.Get("/reports/by-reporter/{id}", nil)
r.Patch("/reports/{id}", nil)
})
}
func MustAdmin(next http.Handler) http.Handler {
fn := func(w http.ResponseWriter, r *http.Request) {
claims, ok := server.ClaimsFromContext(r.Context())
if !ok {
render.Status(r, http.StatusForbidden)
render.JSON(w, r, server.APIError{
Code: server.ErrForbidden,
Message: "Forbidden",
})
return
}
if !claims.UserIsAdmin {
render.Status(r, http.StatusForbidden)
render.JSON(w, r, server.APIError{
Code: server.ErrForbidden,
Message: "Forbidden",
})
return
}
next.ServeHTTP(w, r)
}
return http.HandlerFunc(fn)
}

View file

@ -0,0 +1,19 @@
-- +migrate Up
-- 2023-03-19: Add moderation-related tables
alter table users add column is_admin boolean not null default false;
create table reports (
id serial primary key,
-- we keep deleted users for 180 days after deletion, so it's fine to tie this to a user object
user_id text not null references users (id) on delete cascade,
member_id text null references members (id) on delete set null,
reason text not null,
reporter_id text not null,
created_at timestamptz not null default now(),
resolved_at timestamptz,
admin_id text null references users (id) on delete set null,
admin_comment text
);