Skip to content

Instantly share code, notes, and snippets.

@Anaphylaxis
Created September 12, 2024 18:27
Show Gist options
  • Select an option

  • Save Anaphylaxis/ce827ed75fbc4604a307177658640ef3 to your computer and use it in GitHub Desktop.

Select an option

Save Anaphylaxis/ce827ed75fbc4604a307177658640ef3 to your computer and use it in GitHub Desktop.
Delete Spammers from Gitea (Golang)
// 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
}
@Anaphylaxis
Copy link
Author

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment