add delete rooms function

This commit is contained in:
forest 2023-01-09 11:11:51 -06:00
parent 919490dcc2
commit 923f9b244d
4 changed files with 221 additions and 11 deletions

View file

@ -34,6 +34,7 @@ type FrontendApp struct {
Port int
Domain string
Router *http.ServeMux
DB *DBModel
HTMLTemplates map[string]*template.Template
cssHash string
basicURLPathRegex *regexp.Regexp
@ -47,9 +48,17 @@ type MatrixRoom struct {
IdWithName string
Rows int
Percent int
Ban bool
Status string
}
func initFrontend(config *Config) FrontendApp {
type DeleteProgress struct {
Rooms []MatrixRoom
StateGroupsStateProgress int
}
func initFrontend(config *Config, db *DBModel) FrontendApp {
cssBytes, err := os.ReadFile(filepath.Join(".", "frontend", "static", "app.css"))
if err != nil {
@ -62,6 +71,7 @@ func initFrontend(config *Config) FrontendApp {
Port: config.FrontendPort,
Domain: config.FrontendDomain,
Router: http.NewServeMux(),
DB: db,
HTMLTemplates: map[string]*template.Template{},
basicURLPathRegex: regexp.MustCompile("(?i)[a-z0-9/?&_+-]+"),
base58Regex: regexp.MustCompile("(?i)[a-z0-9_-]+"),
@ -74,14 +84,43 @@ func initFrontend(config *Config) FrontendApp {
userIsLoggedIn := session.UserID != ""
if userIsLoggedIn {
deleteProgress, err := ReadJsonFile[DeleteProgress]("data/deleteRooms.json")
if err != nil {
(*session.Flash)["error"] = "an error occurred reading deleteRooms json"
}
if deleteProgress.Rooms != nil && len(deleteProgress.Rooms) > 0 {
app.buildPageFromTemplate(responseWriter, request, session, "deleting.html", deleteProgress)
return
}
if request.Method == "POST" {
toDelete := []MatrixRoom{}
for i := 0; i < 20; i++ {
roomId := request.PostFormValue(fmt.Sprintf("id_%d", i))
delete := request.PostFormValue(fmt.Sprintf("delete_%d", i))
ban := request.PostFormValue(fmt.Sprintf("ban_%d", i))
log.Printf("%s %s %s", roomId, delete, ban)
if roomId != "" && (delete != "" || ban != "") {
toDelete = append(toDelete, MatrixRoom{
Id: roomId,
Ban: ban != "",
IdWithName: fmt.Sprintf("%s: %s", roomId, app.getMatrixRoomNameWithCache(roomId)),
Status: "...",
})
}
}
err := WriteJsonFile("data/deleteRooms.json", DeleteProgress{
Rooms: toDelete,
})
if err != nil {
(*session.Flash)["error"] = "an error occurred saving deleteRooms json"
}
go doRoomDeletes(app.DB)
http.Redirect(responseWriter, request, "/", http.StatusFound)
return
}
diskUsage, err := os.ReadFile("data/diskUsage.json")
@ -117,10 +156,7 @@ func initFrontend(config *Config) FrontendApp {
bigRoomsRowCount := 0
for i, room := range biggestRooms {
// TODO cache this ??
name, err := matrixAdmin.GetRoomName(room.Id)
if err != nil {
log.Printf("error getting name for '%s': %s\n", room.Id, err)
}
name := app.getMatrixRoomNameWithCache(room.Id)
biggestRooms[i] = MatrixRoom{
Id: room.Id,
Name: name,
@ -405,3 +441,17 @@ func (app *FrontendApp) reloadTemplates() {
}
}
func (app *FrontendApp) getMatrixRoomNameWithCache(id string) string {
nameFromCache, hasNameFromCache := app.roomNameCache[id]
if hasNameFromCache {
return nameFromCache
}
name, err := matrixAdmin.GetRoomName(id)
if err != nil {
log.Printf("error getting name for '%s': %s\n", id, err)
} else {
app.roomNameCache[id] = name
}
return name
}

View file

@ -0,0 +1,22 @@
<div class="horizontal justify-center wrap">
<h3>deleting...</h3>
<div>
{{ range $i, $room := .Rooms }}
{{ if $room.Id }}
<div class="horizontal align-center">
<label for="ban_{{ $i }}" >BAN</span>
<input id="ban_{{ $i }}" type="checkbox" disabled value="{{if $room.Ban }}on{{ end }}"></input>
<span> STATUS: {{ $room.Status }} &nbsp; {{ $room.IdWithName }}</span>
</div>
{{ end }}
{{ end }}
</div>
{{ if .StateGroupsStateProgress > 0 }}
<p>
Progress of deleting rows from <code>state_groups_state</code>: {{ .StateGroupsStateProgress }}%
</p>
{{ end }}
</div>

144
main.go
View file

@ -1,9 +1,11 @@
package main
import (
"fmt"
"log"
"os"
"reflect"
"sort"
"strings"
"sync"
"time"
@ -36,6 +38,7 @@ type DiskUsage struct {
}
var isRunningScheduledTask bool
var isDoingDeletes bool
var mutex sync.Mutex
var matrixAdmin *MatrixAdmin
@ -56,7 +59,7 @@ func main() {
db := initDatabase(&config)
matrixAdmin = initMatrixAdmin(&config)
frontend := initFrontend(&config)
frontend := initFrontend(&config, db)
log.Printf("🧹 matrix-synapse-diskspace-janitor is about to try to start listening on :%d\n", config.FrontendPort)
go frontend.ListenAndServe()
@ -149,7 +152,7 @@ func runScheduledTask(db *DBModel, config *Config) {
}
}
err = WriteJsonFile[map[string]int]("data/stateGroupsStateRowCountByRoom.json", rowCountByRoom)
err = WriteJsonFile("data/stateGroupsStateRowCountByRoom.json", rowCountByRoom)
if err != nil {
log.Printf("ERROR!: runScheduledTask can't write data/stateGroupsStateRowCountByRoom.json: %s\n", err)
}
@ -163,7 +166,7 @@ func runScheduledTask(db *DBModel, config *Config) {
janitorState.LastScheduledTaskRunUnixMilli = time.Now().UnixMilli()
err = WriteJsonFile[JanitorState]("data/janitorState.json", janitorState)
err = WriteJsonFile("data/janitorState.json", janitorState)
if err != nil {
log.Printf("ERROR!: runScheduledTask can't write data/janitorState.json: %s\n", err)
}
@ -172,6 +175,141 @@ func runScheduledTask(db *DBModel, config *Config) {
isRunningScheduledTask = false
}
func doRoomDeletes(db *DBModel) {
if isDoingDeletes {
log.Println("doRoomDeletes(): isDoingDeletes already!")
return
}
isDoingDeletes = true
defer func() {
isDoingDeletes = false
}()
deleteProgress, err := ReadJsonFile[DeleteProgress]("data/deleteRooms.json")
if err != nil {
log.Println("doRoomDeletes(): Can't do room deletes because can't read deleteRooms.json")
return
}
if deleteProgress.Rooms == nil || len(deleteProgress.Rooms) == 0 {
log.Println("doRoomDeletes(): Can't do room deletes because no rooms to delete")
return
}
log.Printf("doRoomDeletes(): starting to delete %d rooms\n", len(deleteProgress.Rooms))
for _, room := range deleteProgress.Rooms {
err := matrixAdmin.DeleteRoom(room.Id, room.Ban)
if err != nil {
log.Printf("doRoomDeletes(): Can't do room deletes because deleting %s returned %s\n", room.Id, err)
return
}
}
log.Printf("doRoomDeletes(): waiting for %d rooms to be done deleting...\n", len(deleteProgress.Rooms))
isDoneWaitingForRoomDeletesToFinish := false
for !isDoneWaitingForRoomDeletesToFinish {
allRoomsDeletionComplete := true
for i, room := range deleteProgress.Rooms {
// TODO do something with the users that this returns? i.e. re-add them to the room later?
status, _, err := matrixAdmin.GetDeleteRoomStatus(room.Id)
if err != nil {
log.Printf("doRoomDeletes(): Can't do room deletes because GetDeleteRoomStatus('%s') returned %s\n", room.Id, err)
return
}
deleteProgress.Rooms[i] = MatrixRoom{
Id: room.Id,
IdWithName: room.IdWithName,
Ban: room.Ban,
Status: status,
}
if status != "complete" {
allRoomsDeletionComplete = false
}
}
err = WriteJsonFile("data/deleteRooms.json", deleteProgress)
if err != nil {
log.Println("doRoomDeletes(): Can't do room deletes because can't write deleteRooms.json")
return
}
if allRoomsDeletionComplete {
isDoneWaitingForRoomDeletesToFinish = true
} else {
time.Sleep(time.Second * 5)
}
}
log.Printf("doRoomDeletes(): getting state group ids for %d rooms...\n", len(deleteProgress.Rooms))
allStateGroupsToDelete := []int64{}
for _, room := range deleteProgress.Rooms {
stateGroups, err := db.GetStateGroupsForRoom(room.Id)
if err != nil {
log.Printf("doRoomDeletes(): Can't do room deletes because getting state group ids for %s returned %s\n", room.Id, err)
return
}
allStateGroupsToDelete = append(allStateGroupsToDelete, stateGroups...)
}
sort.Slice(allStateGroupsToDelete, func(i, j int) bool {
return allStateGroupsToDelete[i] < allStateGroupsToDelete[j]
})
allStateGroupsToDeleteFile, err := os.OpenFile("data/stateGroupsToDelete.txt", os.O_CREATE|os.O_WRONLY, 0644)
for _, stateGroupId := range allStateGroupsToDelete {
fmt.Fprintf(allStateGroupsToDeleteFile, "%d\n", stateGroupId)
}
allStateGroupsToDeleteFile.Close()
log.Printf("doRoomDeletes(): deleting %d state groups from state_groups_state...\n", len(allStateGroupsToDelete))
statusChannel := db.DeleteStateGroupsState(allStateGroupsToDelete, 0)
lastUpdateTime := time.Now()
for status := range statusChannel {
if time.Since(lastUpdateTime) > time.Second*5 {
lastUpdateTime = time.Now()
deleteProgress.StateGroupsStateProgress = int((float64(status.StateGroupsDeleted) / float64(len(allStateGroupsToDelete))) * float64(100))
log.Printf(
"doRoomDeletes(): %d/%d (%d rows) (%d errors) (%d%s)\n",
status.StateGroupsDeleted, len(allStateGroupsToDelete), status.RowsDeleted,
status.Errors, deleteProgress.StateGroupsStateProgress, "%",
)
err = WriteJsonFile("data/deleteRooms.json", deleteProgress)
if err != nil {
log.Println("doRoomDeletes(): Can't do room deletes because can't write deleteRooms.json")
return
}
}
}
log.Println("doRoomDeletes(): deleting from state_groups_state complete! now cleaning up state_groups...")
totalStateGroupRows := 0
for _, room := range deleteProgress.Rooms {
rowsDeleted, err := db.DeleteStateGroupsForRoom(room.Id)
if err != nil {
log.Printf("doRoomDeletes(): DeleteStateGroupsForRoom('%s') returned %s\n", room.Id, err)
}
totalStateGroupRows += int(rowsDeleted)
}
log.Printf("doRoomDeletes(): %d state_groups related rows deleted. \n", totalStateGroupRows)
err = os.Remove("data/deleteRooms.json")
if err != nil {
log.Printf("doRoomDeletes(): failed to remove deleteRooms.json: %s\n", err)
}
log.Println("doRoomDeletes(): completed successfully!!")
}
func validateConfig(config *Config) {
errors := []string{}

View file

@ -88,10 +88,10 @@ func initMatrixAdmin(config *Config) *MatrixAdmin {
// curl -H "Content-Type: application/json" -X DELETE "localhost:8008/_synapse/admin/v2/rooms/$roomid?access_token=xxxxxxxxx" \
// --data '{ "block": false, "force_purge": true, "purge": true, "message": "This room is being cleaned, stand by..." }'
func (admin *MatrixAdmin) DeleteRoom(roomId string) error {
func (admin *MatrixAdmin) DeleteRoom(roomId string, ban bool) error {
deleteRequestBodyObject := DeleteRoomRequest{
Block: false,
Block: ban,
ForcePurge: true,
Purge: true,
Message: "This room is being cleaned, stand by...",