Improve time complexity

This commit is contained in:
rxdn 2022-06-22 20:42:36 +01:00
parent 49ce52283c
commit 1ec71e586a
5 changed files with 69 additions and 91 deletions

View File

@ -1,57 +1,28 @@
package api package api
import ( import (
"context"
"fmt"
"github.com/TicketsBot/GoPanel/utils" "github.com/TicketsBot/GoPanel/utils"
"github.com/TicketsBot/common/permission"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"golang.org/x/sync/errgroup"
"strconv" "strconv"
"strings"
) )
func GetPermissionLevel(ctx *gin.Context) { func GetPermissionLevel(ctx *gin.Context) {
userId := ctx.Keys["userid"].(uint64) userId := ctx.Keys["userid"].(uint64)
guilds := strings.Split(ctx.Query("guilds"), ",") guildId, err := strconv.ParseUint(ctx.Query("guild"), 10, 64)
if len(guilds) > 100 {
ctx.JSON(400, gin.H{
"success": false,
"error": "too many guilds",
})
return
}
// TODO: This is insanely inefficient
levels := make(map[string]permission.PermissionLevel)
group, _ := errgroup.WithContext(context.Background())
for _, raw := range guilds {
guildId, err := strconv.ParseUint(raw, 10, 64)
if err != nil { if err != nil {
ctx.JSON(400, gin.H{ ctx.JSON(400, utils.ErrorStr("Invalid guild ID"))
"success": false,
"error": fmt.Sprintf("invalid guild id: %s", raw),
})
return return
} }
group.Go(func() error { permissionLevel, err := utils.GetPermissionLevel(guildId, userId)
level, err := utils.GetPermissionLevel(guildId, userId) if err != nil {
levels[strconv.FormatUint(guildId, 10)] = level
return err
})
}
if err := group.Wait(); err != nil {
ctx.JSON(500, utils.ErrorJson(err)) ctx.JSON(500, utils.ErrorJson(err))
return return
} }
ctx.JSON(200, gin.H{ ctx.JSON(200, gin.H{
"success": true, "success": true,
"levels": levels, "permission_level": permissionLevel,
}) })
} }

View File

@ -2,14 +2,14 @@ package api
import ( import (
"context" "context"
"github.com/TicketsBot/GoPanel/database" dbclient "github.com/TicketsBot/GoPanel/database"
"github.com/TicketsBot/GoPanel/rpc/cache" "github.com/TicketsBot/GoPanel/rpc/cache"
"github.com/TicketsBot/GoPanel/utils" "github.com/TicketsBot/GoPanel/utils"
"github.com/TicketsBot/common/permission" "github.com/TicketsBot/common/permission"
syncutils "github.com/TicketsBot/common/utils" syncutils "github.com/TicketsBot/common/utils"
"github.com/TicketsBot/database"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/jackc/pgx/v4" "github.com/jackc/pgtype"
"github.com/rxdn/gdl/objects/guild"
"github.com/rxdn/gdl/rest/request" "github.com/rxdn/gdl/rest/request"
"golang.org/x/sync/errgroup" "golang.org/x/sync/errgroup"
"sort" "sort"
@ -24,49 +24,52 @@ type wrappedGuild struct {
func GetGuilds(ctx *gin.Context) { func GetGuilds(ctx *gin.Context) {
userId := ctx.Keys["userid"].(uint64) userId := ctx.Keys["userid"].(uint64)
guilds, err := database.Client.UserGuilds.Get(userId) // Get all guilds the user is in
guilds, err := dbclient.Client.UserGuilds.Get(userId)
if err != nil {
ctx.JSON(500, utils.ErrorJson(err))
return
}
// Get the subset of guilds that the user is in that the bot is also in
guildIds := make([]uint64, len(guilds))
guildMap := make(map[uint64]database.UserGuild) // Make a map of all guilds for O(1) access
for i, guild := range guilds {
guildIds[i] = guild.GuildId
guildMap[guild.GuildId] = guild
}
botGuilds, err := getExistingGuilds(guildIds)
if err != nil { if err != nil {
ctx.JSON(500, utils.ErrorJson(err)) ctx.JSON(500, utils.ErrorJson(err))
return return
} }
wg := syncutils.NewChannelWaitGroup() wg := syncutils.NewChannelWaitGroup()
wg.Add(len(guilds)) wg.Add(len(botGuilds))
group, _ := errgroup.WithContext(context.Background()) group, _ := errgroup.WithContext(context.Background())
ch := make(chan wrappedGuild) ch := make(chan wrappedGuild)
for _, g := range guilds { for _, guildId := range botGuilds {
g := g guildId := guildId
g := guildMap[guildId]
group.Go(func() error { group.Go(func() error {
defer wg.Done() defer wg.Done()
// verify bot is in guild // Determine the user's permission level in this guild
if err := cache.Instance.QueryRow(context.Background(), `SELECT 1 from guilds WHERE "guild_id" = $1`, g.GuildId).Scan(nil); err != nil { var permLevel permission.PermissionLevel
if err == pgx.ErrNoRows {
return nil
} else {
return err
}
}
fakeGuild := guild.Guild{
Id: g.GuildId,
Owner: g.Owner,
Permissions: g.UserPermissions,
}
if g.Owner { if g.Owner {
fakeGuild.OwnerId = userId permLevel = permission.Admin
} } else {
permLevel, err = utils.GetPermissionLevel(g.GuildId, userId)
permLevel, err := utils.GetPermissionLevel(g.GuildId, userId)
if err != nil { if err != nil {
// If a Discord error occurs, just skip the server // If a Discord error occurs, just skip the server
if _, ok := err.(request.RestError); !ok { if _, ok := err.(request.RestError); !ok {
return err return err
} }
} }
}
if permLevel >= permission.Support { if permLevel >= permission.Support {
wrapped := wrappedGuild{ wrapped := wrappedGuild{
@ -96,7 +99,10 @@ func GetGuilds(ctx *gin.Context) {
return nil return nil
}) })
_ = group.Wait() // error not possible if err := group.Wait(); err != nil {
ctx.JSON(500, utils.ErrorJson(err))
return
}
// sort // sort
sort.Slice(adminGuilds, func(i, j int) bool { sort.Slice(adminGuilds, func(i, j int) bool {
@ -106,24 +112,30 @@ func GetGuilds(ctx *gin.Context) {
ctx.JSON(200, adminGuilds) ctx.JSON(200, adminGuilds)
} }
/*func getAdminGuilds(userId uint64) ([]uint64, error) { func getExistingGuilds(userGuilds []uint64) ([]uint64, error) {
var guilds []uint64 query := `SELECT "guild_id" from guilds WHERE "guild_id" = ANY($1);`
// get guilds owned by user userGuildsArray := &pgtype.Int8Array{}
query := `SELECT "guild_id" FROM guilds WHERE "data"->'owner_id' = '$1';` if err := userGuildsArray.Set(userGuilds); err != nil {
rows, err := cache.Instance.Query(context.Background(), query, userId) return nil, err
}
rows, err := cache.Instance.Query(context.Background(), query, userGuildsArray)
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer rows.Close()
var existingGuilds []uint64
for rows.Next() { for rows.Next() {
var guildId uint64 var guildId uint64
if err := rows.Scan(&guildId); err != nil { if err := rows.Scan(&guildId); err != nil {
return nil, err return nil, err
} }
guilds = append(guilds, guildId) existingGuilds = append(existingGuilds, guildId)
} }
database.Client.Permissions.GetSupport() return existingGuilds, nil
}*/ }

View File

@ -4,6 +4,7 @@ import (
"context" "context"
"github.com/TicketsBot/GoPanel/database" "github.com/TicketsBot/GoPanel/database"
"github.com/TicketsBot/GoPanel/rpc/cache" "github.com/TicketsBot/GoPanel/rpc/cache"
"github.com/TicketsBot/GoPanel/utils"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/rxdn/gdl/objects/user" "github.com/rxdn/gdl/objects/user"
"golang.org/x/sync/errgroup" "golang.org/x/sync/errgroup"
@ -19,10 +20,7 @@ func GetTickets(ctx *gin.Context) {
tickets, err := database.Client.Tickets.GetGuildOpenTickets(guildId) tickets, err := database.Client.Tickets.GetGuildOpenTickets(guildId)
if err != nil { if err != nil {
ctx.AbortWithStatusJSON(500, gin.H{ ctx.JSON(500, utils.ErrorJson(err))
"success": false,
"error": err.Error(),
})
return return
} }
@ -50,10 +48,7 @@ func GetTickets(ctx *gin.Context) {
} }
if err := group.Wait(); err != nil { if err := group.Wait(); err != nil {
ctx.AbortWithStatusJSON(500, gin.H{ ctx.JSON(500, utils.ErrorJson(err))
"success": false,
"error": err.Error(),
})
return return
} }

View File

@ -38,8 +38,8 @@
} }
async function goto(guildId) { async function goto(guildId) {
const permissionLevels = await getPermissionLevel(guildId); const permissionLevel = await getPermissionLevel(guildId);
if (permissionLevels[guildId] === 2) { if (permissionLevel === 2) {
window.location.href = `/manage/${guildId}/settings`; window.location.href = `/manage/${guildId}/settings`;
} else { } else {
window.location.href = `/manage/${guildId}/transcripts`; window.location.href = `/manage/${guildId}/transcripts`;
@ -47,13 +47,13 @@
} }
async function getPermissionLevel(guildId) { async function getPermissionLevel(guildId) {
const res = await axios.get(`${API_URL}/user/permissionlevel?guilds=${guildId}`); const res = await axios.get(`${API_URL}/user/permissionlevel?guild=${guildId}`);
if (res.status !== 200 || !res.data.success) { if (res.status !== 200 || !res.data.success) {
notifyError(res.data.error); notifyError(res.data.error);
return; return;
} }
return res.data.levels; return res.data.permission_level;
} }
</script> </script>

View File

@ -5,11 +5,11 @@ import (
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
) )
func ErrorJson(err error) map[string]interface{} { func ErrorJson(err error) map[string]any {
return ErrorStr(err.Error()) return ErrorStr(err.Error())
} }
func ErrorStr(err string, format ...interface{}) map[string]interface{} { func ErrorStr(err string, format ...any) map[string]any {
return gin.H{ return gin.H{
"success": false, "success": false,
"error": fmt.Sprintf(err, format...), "error": fmt.Sprintf(err, format...),