forked from mirrors/pronouns.cc
start reports/moderation in backend
This commit is contained in:
parent
41edaee8ea
commit
33f903b07d
8 changed files with 136 additions and 2 deletions
47
backend/db/report.go
Normal file
47
backend/db/report.go
Normal 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
|
||||
}
|
|
@ -34,6 +34,7 @@ type User struct {
|
|||
FediverseInstance *string
|
||||
|
||||
MaxInvites int
|
||||
IsAdmin bool
|
||||
|
||||
DeletedAt *time.Time
|
||||
SelfDelete *bool
|
||||
|
|
|
@ -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)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
13
backend/routes/mod/get_reports.go
Normal file
13
backend/routes/mod/get_reports.go
Normal 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
|
||||
}
|
52
backend/routes/mod/routes.go
Normal file
52
backend/routes/mod/routes.go
Normal 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)
|
||||
}
|
19
scripts/migrate/010_reports.sql
Normal file
19
scripts/migrate/010_reports.sql
Normal 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
|
||||
);
|
Loading…
Reference in a new issue