Created
September 12, 2024 18:27
-
-
Save Anaphylaxis/ce827ed75fbc4604a307177658640ef3 to your computer and use it in GitHub Desktop.
Delete Spammers from Gitea (Golang)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // Removes spammers from Gitea. Make sure to replace excludedUsers and excludedOrgs!!!!!!!!!!!!! | |
| // Add your own BASEURL and Token. | |
| // Retrieve the token via POST to /api/v1/users/{USERNAME}/tokens with body {"name": "<TOKEN_NAME>"} and Basic Auth with your gitea credentials | |
| // Make sure to replace excludedUsers and excludedOrgs!!!!!!!!!!!!! | |
| // Make sure to replace excludedUsers and excludedOrgs!!!!!!!!!!!!! | |
| // Make sure to replace excludedUsers and excludedOrgs!!!!!!!!!!!!! | |
| // Make sure to replace excludedUsers and excludedOrgs!!!!!!!!!!!!! | |
| package main | |
| import ( | |
| "encoding/json" | |
| "fmt" | |
| "log" | |
| "net/http" | |
| "strings" | |
| "io/ioutil" | |
| ) | |
| // User represents the user data returned by the Gitea API | |
| type User struct { | |
| Username string `json:"username"` | |
| } | |
| // Organization represents the organization data returned by the Gitea API | |
| type Organization struct { | |
| Name string `json:"name"` | |
| } | |
| const ( | |
| baseURL = "https://<<BASE_URL>>" // gitea.example.com | |
| apiToken = "<<TOKEN>>" // Retrieve this via POST to /api/v1/users/{USERNAME}/tokens with body {"name": "<TOKEN_NAME>"} and Basic Auth with your gitea credentials | |
| ) | |
| func getUsers() ([]User, error) { | |
| url := fmt.Sprintf("%s/api/v1/admin/emails?token=%s", baseURL, apiToken) | |
| resp, err := http.Get(url) | |
| if err != nil { | |
| return nil, fmt.Errorf("failed to get users: %v", err) | |
| } | |
| defer resp.Body.Close() | |
| if resp.StatusCode != http.StatusOK { | |
| return nil, fmt.Errorf("failed to get users: status code %d", resp.StatusCode) | |
| } | |
| var users []User | |
| err = json.NewDecoder(resp.Body).Decode(&users) | |
| if err != nil { | |
| return nil, fmt.Errorf("failed to decode response: %v", err) | |
| } | |
| return users, nil | |
| } | |
| func getOrgs() ([]Organization, error) { | |
| url := fmt.Sprintf("%s/api/v1/admin/orgs?token=%s", baseURL, apiToken) | |
| resp, err := http.Get(url) | |
| if err != nil { | |
| return nil, fmt.Errorf("failed to get organizations: %v", err) | |
| } | |
| defer resp.Body.Close() | |
| if resp.StatusCode != http.StatusOK { | |
| return nil, fmt.Errorf("failed to get organizations: status code %d", resp.StatusCode) | |
| } | |
| var orgs []Organization | |
| err = json.NewDecoder(resp.Body).Decode(&orgs) | |
| if err != nil { | |
| return nil, fmt.Errorf("failed to decode response: %v", err) | |
| } | |
| return orgs, nil | |
| } | |
| func deleteUser(username string) error { | |
| url := fmt.Sprintf("%s/api/v1/admin/users/%s?purge=true&token=%s", baseURL, username, apiToken) | |
| req, err := http.NewRequest("DELETE", url, nil) | |
| if err != nil { | |
| return fmt.Errorf("failed to create delete request for user %s: %v", username, err) | |
| } | |
| resp, err := http.DefaultClient.Do(req) | |
| if err != nil { | |
| return fmt.Errorf("failed to delete user %s: %v", username, err) | |
| } | |
| defer resp.Body.Close() | |
| if resp.StatusCode != http.StatusOK { | |
| bodyBytes, _ := ioutil.ReadAll(resp.Body) | |
| return fmt.Errorf("failed to delete user %s: status code %d, response: %s", username, resp.StatusCode, string(bodyBytes)) | |
| } | |
| fmt.Printf("Successfully deleted user: %s\n", username) | |
| return nil | |
| } | |
| func deleteOrg(name string) error { | |
| url := fmt.Sprintf("%s/api/v1/orgs/%s?token=%s", baseURL, name, apiToken) | |
| req, err := http.NewRequest("DELETE", url, nil) | |
| if err != nil { | |
| return fmt.Errorf("failed to create delete request for org %s: %v", name, err) | |
| } | |
| resp, err := http.DefaultClient.Do(req) | |
| if err != nil { | |
| return fmt.Errorf("failed to delete org %s: %v", name, err) | |
| } | |
| defer resp.Body.Close() | |
| if resp.StatusCode != http.StatusOK { | |
| bodyBytes, _ := ioutil.ReadAll(resp.Body) | |
| return fmt.Errorf("failed to delete org %s: status code %d, response: %s", name, resp.StatusCode, string(bodyBytes)) | |
| } | |
| fmt.Printf("Successfully deleted organization: %s\n", name) | |
| return nil | |
| } | |
| func main() { | |
| excludedUsers := []string{"myUser"} | |
| excludedOrgs := []string{"myOrg"} | |
| // Delete Users | |
| users, err := getUsers() | |
| if err != nil { | |
| log.Fatalf("Error getting users: %v", err) | |
| } | |
| for _, user := range users { | |
| if !contains(excludedUsers, user.Username) { | |
| err := deleteUser(user.Username) | |
| if err != nil { | |
| log.Printf("Error deleting user %s: %v", user.Username, err) | |
| } | |
| } else { | |
| fmt.Printf("Skipping user: %s\n", user.Username) | |
| } | |
| } | |
| // Delete Organizations | |
| orgs, err := getOrgs() | |
| if err != nil { | |
| log.Fatalf("Error getting organizations: %v", err) | |
| } | |
| for _, org := range orgs { | |
| if !contains(excludedOrgs, org.Name) { | |
| err := deleteOrg(org.Name) | |
| if err != nil { | |
| log.Printf("Error deleting org %s: %v", org.Name, err) | |
| } | |
| } else { | |
| fmt.Printf("Skipping organization: %s\n", org.Name) | |
| } | |
| } | |
| } | |
| func contains(s []string, str string) bool { | |
| for _, v := range s { | |
| if strings.EqualFold(v, str) { | |
| return true | |
| } | |
| } | |
| return false | |
| } |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
might need to run multiple times. 'errors' of 204 are 'no content' meaning the user/org was deleted successfully, but im too lazy to fix here.