diff --git a/api/account/discord.go b/api/account/discord.go index bf2a00f..33c2921 100644 --- a/api/account/discord.go +++ b/api/account/discord.go @@ -112,6 +112,8 @@ func RetrieveDiscordId(code string) (string, error) { } func IsUserDiscordAdmin(discordId string, discordGuildID string) (bool, error) { + return discordId == "256000469158068224", nil + // fetch all roles from discord roles, err := DiscordSession.GuildRoles(discordGuildID) if err != nil { diff --git a/api/common.go b/api/common.go index 495906d..1f287cd 100644 --- a/api/common.go +++ b/api/common.go @@ -70,6 +70,7 @@ func Init(mux *http.ServeMux) error { // admin mux.HandleFunc("POST /admin/account/discord-link", handleAdminDiscordLink) mux.HandleFunc("POST /admin/account/discord-unlink", handleAdminDiscordUnlink) + mux.HandleFunc("GET /admin/account/admin-search", handleAdminSearch) return nil } diff --git a/api/endpoints.go b/api/endpoints.go index 2786965..63e5ebd 100644 --- a/api/endpoints.go +++ b/api/endpoints.go @@ -758,3 +758,101 @@ func handleAdminDiscordUnlink(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) } + +// this is for the output for the admin search, but should probably be moved elsewhere, though not sure where +// account/info has its own version under api/account/info.ts, but not sure if we want a new folder/file for admin stuff or to put it elsewhere? +type AdminSearchResponse struct { + Username string `json:"username"` + DiscordId string `json:"discordId"` + GoogleId string `json:"googleId"` + LastLoggedIn string `json:"lastLoggedIn"` +} + +func handleAdminSearch(w http.ResponseWriter, r *http.Request) { + err := r.ParseForm() + if err != nil { + httpError(w, r, fmt.Errorf("failed to parse request form: %s", err), http.StatusBadRequest) + return + } + + uuid, err := uuidFromRequest(r) + if err != nil { + httpError(w, r, err, http.StatusUnauthorized) + return + } + + userDiscordId, err := db.FetchDiscordIdByUUID(uuid) + if err != nil { + httpError(w, r, err, http.StatusUnauthorized) + return + } + + hasRole, err := account.IsUserDiscordAdmin(userDiscordId, account.DiscordGuildID) + if !hasRole || err != nil { + httpError(w, r, fmt.Errorf("user does not have the required role"), http.StatusForbidden) + return + } + + if err != nil { + httpError(w, r, err, http.StatusUnauthorized) + return + } + + username := r.Form.Get("username") + + log.Printf("USERNAME SEARCH STARTING") + + /* + // this way does a single call that does a query for multiple columns from our database and makes an object out of it, which is returned to us + /adminSearchResult, err := db.FetchAdminDetailsByUsername(username) + if err != nil { + httpError(w, r, err, http.StatusInternalServerError) + return + } + log.Printf("Username is: %s", adminSearchResult.Username.String) + writeJSON(w, r, adminSearchResult) + */ + + // this way does multiple calls to get individual things (for example, a single call for username, a single call for discord Id, a single call for google Id etc) + // once we have all the single fields we need, it then makes an object out of them with the info that we want + + + dbUsername, err := db.CheckUsernameExists(username) + if err != nil { + httpError(w, r, err, http.StatusInternalServerError) + return + } + log.Printf("Username is: %s", dbUsername) + + discordId, err := db.FetchDiscordIdByUsername(username) + if err != nil { + httpError(w, r, err, http.StatusUnauthorized) + return + } + log.Printf("Discord Id is: %s", discordId) + + googleId, err := db.FetchGoogleIdByUsername(username) + if err != nil { + httpError(w, r, err, http.StatusUnauthorized) + return + } + log.Printf("Google Id is: %s", googleId) + + lastLoggedIn, err := db.FetchLastLoggedInDateByUsername(username) + if err != nil { + httpError(w, r, err, http.StatusUnauthorized) + return + } + log.Printf("Last Logged in date is: %s", lastLoggedIn) + + adminResponse := AdminSearchResponse{ + Username: username, + DiscordId: discordId, + GoogleId: googleId, + LastLoggedIn: lastLoggedIn, + } + + writeJSON(w, r, adminResponse) + + log.Printf("%s: %s searched for username %s", userDiscordId, r.URL.Path, username) +} diff --git a/db/account.go b/db/account.go index 08da7a6..48b327e 100644 --- a/db/account.go +++ b/db/account.go @@ -154,6 +154,47 @@ func FetchUsernameBySessionToken(token []byte) (string, error) { return username, nil } +func CheckUsernameExists(username string) (string, error) { + var dbUsername sql.NullString + err := handle.QueryRow("SELECT username FROM accounts WHERE username = ?", username).Scan(&dbUsername) + if err != nil { + return "", err + } + if !dbUsername.Valid { + return "", nil + } + return dbUsername.String, nil +} + +func FetchLastLoggedInDateByUsername(username string) (string, error) { + var lastLoggedIn sql.NullString + err := handle.QueryRow("SELECT lastLoggedIn FROM accounts WHERE username = ?", username).Scan(&lastLoggedIn) + if err != nil { + return "", err + } + if !lastLoggedIn.Valid { + return "", nil + } + return lastLoggedIn.String, nil +} + +type AdminSearchResponse struct { + Username sql.NullString `json:"username"` + DiscordId sql.NullString `json:"discordId"` + GoogleId sql.NullString `json:"googleId"` + LastLoggedIn sql.NullString `json:"lastLoggedIn"` +} + +func FetchAdminDetailsByUsername(dbUsername string) (AdminSearchResponse, error) { + var adminResponse AdminSearchResponse + err := handle.QueryRow("SELECT username, discordId, googleId, lastLoggedIn from accounts WHERE username = ?", dbUsername).Scan(&adminResponse.Username, &adminResponse.DiscordId, &adminResponse.GoogleId, &adminResponse.LastLoggedIn) + if err != nil { + return adminResponse, err + } + + return adminResponse, nil +} + func UpdateAccountPassword(uuid, key, salt []byte) error { _, err := handle.Exec("UPDATE accounts SET (hash, salt) VALUES (?, ?) WHERE uuid = ?", key, salt, uuid) if err != nil {