184 lines
4.3 KiB
Go
184 lines
4.3 KiB
Go
package cleandb
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"os"
|
|
"time"
|
|
|
|
dbpkg "codeberg.org/pronounscc/pronouns.cc/backend/db"
|
|
"codeberg.org/pronounscc/pronouns.cc/backend/log"
|
|
"github.com/georgysavva/scany/v2/pgxscan"
|
|
"github.com/jackc/pgx/v5"
|
|
"github.com/joho/godotenv"
|
|
"github.com/rs/xid"
|
|
"github.com/urfave/cli/v2"
|
|
)
|
|
|
|
var Command = &cli.Command{
|
|
Name: "clean",
|
|
Usage: "Clean deleted tokens + users daily",
|
|
Action: run,
|
|
}
|
|
|
|
func run(c *cli.Context) error {
|
|
err := godotenv.Load()
|
|
if err != nil {
|
|
fmt.Println("error loading .env file:", err)
|
|
return err
|
|
}
|
|
|
|
changeUnusedUsernames := os.Getenv("DB_CLEAN_CHANGE_UNUSED_USERNAMES") == "true"
|
|
|
|
ctx := context.Background()
|
|
|
|
db, err := dbpkg.New()
|
|
if err != nil {
|
|
fmt.Println("error opening database:", err)
|
|
return err
|
|
}
|
|
defer db.Close()
|
|
|
|
fmt.Println("opened database")
|
|
|
|
fmt.Println("deleting invalidated tokens")
|
|
|
|
ct, err := db.Exec(ctx, "DELETE FROM tokens WHERE invalidated = true OR expires < $1", time.Now())
|
|
if err != nil {
|
|
fmt.Println("executing query:", err)
|
|
return err
|
|
}
|
|
|
|
fmt.Printf("deleted %v invalidated or expired tokens\n", ct.RowsAffected())
|
|
|
|
fmt.Println("deleting expired export files")
|
|
var exports []dbpkg.DataExport
|
|
err = pgxscan.Select(ctx, db, &exports, "SELECT * FROM data_exports WHERE created_at < $1", time.Now().Add(-dbpkg.KeepExportTime))
|
|
if err != nil {
|
|
fmt.Println("error getting to-be-deleted export files:", err)
|
|
return err
|
|
}
|
|
|
|
for _, de := range exports {
|
|
err = db.DeleteExport(ctx, de)
|
|
if err != nil {
|
|
fmt.Printf("error deleting export %v: %v\n", de.ID, err)
|
|
continue
|
|
}
|
|
|
|
fmt.Println("deleted export", de.ID)
|
|
}
|
|
|
|
fmt.Printf("deleted %v expired exports\n", len(exports))
|
|
|
|
if changeUnusedUsernames {
|
|
fmt.Println("cleaning unused usernames")
|
|
|
|
tx, err := db.Begin(ctx)
|
|
if err != nil {
|
|
fmt.Printf("error starting transaction: %v\n", err)
|
|
return err
|
|
}
|
|
defer func() {
|
|
err := tx.Rollback(ctx)
|
|
if err != nil && !errors.Is(err, pgx.ErrTxClosed) {
|
|
log.Error("rolling back transaction:", err)
|
|
}
|
|
}()
|
|
|
|
inactiveUsers, err := db.InactiveUsers(ctx, tx)
|
|
if err != nil {
|
|
fmt.Printf("getting inactive users: %v\n", err)
|
|
return err
|
|
}
|
|
|
|
for _, u := range inactiveUsers {
|
|
err = db.UpdateUsername(ctx, tx, u.ID, fmt.Sprintf("inactive-user-%v", u.SnowflakeID))
|
|
if err != nil {
|
|
fmt.Printf("changing username for user %v: %v\n", u.SnowflakeID, err)
|
|
return err
|
|
}
|
|
}
|
|
|
|
err = tx.Commit(ctx)
|
|
if err != nil {
|
|
fmt.Printf("committing transaction: %v\n", err)
|
|
return err
|
|
}
|
|
|
|
fmt.Printf("changed usernames for %v inactive users\n", len(inactiveUsers))
|
|
} else {
|
|
fmt.Println("not cleaning unused usernames")
|
|
}
|
|
|
|
var users []dbpkg.User
|
|
err = pgxscan.Select(ctx, db, &users, `SELECT * FROM users WHERE
|
|
deleted_at IS NOT NULL AND
|
|
(self_delete = true AND deleted_at < $1)
|
|
OR (self_delete = false AND deleted_at < $2)
|
|
ORDER BY id`, time.Now().Add(-dbpkg.SelfDeleteAfter), time.Now().Add(-dbpkg.ModDeleteAfter))
|
|
if err != nil {
|
|
fmt.Println("error getting to-be-deleted users:", err)
|
|
return err
|
|
}
|
|
|
|
if len(users) == 0 {
|
|
fmt.Println("there are no users pending deletion\nfinished cleaning database!")
|
|
return nil
|
|
}
|
|
|
|
for _, u := range users {
|
|
members, err := db.UserMembers(ctx, u.ID, true)
|
|
if err != nil {
|
|
fmt.Printf("error getting members for user %v: %v\n", u.ID, err)
|
|
continue
|
|
}
|
|
|
|
for _, m := range members {
|
|
if m.Avatar == nil {
|
|
continue
|
|
}
|
|
|
|
fmt.Printf("deleting avatars for member %v\n", m.ID)
|
|
|
|
err = db.DeleteMemberAvatar(ctx, m.ID, *m.Avatar)
|
|
if err != nil {
|
|
fmt.Printf("error deleting avatars for member %v: %v", m.ID, err)
|
|
continue
|
|
}
|
|
|
|
fmt.Printf("deleted avatars for member %v\n", m.ID)
|
|
}
|
|
|
|
if u.Avatar == nil {
|
|
continue
|
|
}
|
|
|
|
fmt.Printf("deleting avatars for user %v\n", u.ID)
|
|
|
|
err = db.DeleteUserAvatar(ctx, u.ID, *u.Avatar)
|
|
if err != nil {
|
|
fmt.Printf("error deleting avatars for user %v: %v", u.ID, err)
|
|
continue
|
|
}
|
|
|
|
fmt.Printf("deleted avatars for user %v\n", u.ID)
|
|
}
|
|
|
|
ids := make([]xid.ID, len(users))
|
|
for _, u := range users {
|
|
ids = append(ids, u.ID)
|
|
}
|
|
|
|
ct, err = db.Exec(ctx, "DELETE FROM users WHERE id = ANY($1)", ids)
|
|
if err != nil {
|
|
fmt.Printf("error deleting users: %v\n", err)
|
|
return err
|
|
}
|
|
|
|
fmt.Printf("deleted %v users!\n", ct.RowsAffected())
|
|
fmt.Println("finished cleaning database!")
|
|
return nil
|
|
}
|