Add refresh guilds button

This commit is contained in:
rxdn 2021-01-05 22:55:44 +00:00
parent 52bd71eab4
commit 3a98761b77
10 changed files with 255 additions and 129 deletions

View File

@ -0,0 +1,74 @@
package api
import (
"fmt"
"github.com/TicketsBot/GoPanel/messagequeue"
"github.com/TicketsBot/GoPanel/utils"
"github.com/TicketsBot/GoPanel/utils/discord"
"github.com/gin-gonic/contrib/sessions"
"github.com/gin-gonic/gin"
"time"
)
func ReloadGuildsHandler(ctx *gin.Context) {
userId := ctx.Keys["userid"].(uint64)
key := fmt.Sprintf("tickets:dashboard:guildreload:%d", userId)
res, err := messagequeue.Client.SetNX(key, 1, time.Second*10).Result()
if err != nil {
ctx.JSON(500, utils.ErrorToResponse(err))
return
}
if !res {
ttl, err := messagequeue.Client.TTL(key).Result()
if err != nil {
ctx.JSON(500, utils.ErrorToResponse(err))
return
}
// handle redis error codes
if ttl < 0 {
ttl = 0
}
ctx.JSON(429, utils.ErrorStr("You're doing this too quickly: try again in %d seconds", int(ttl.Seconds())))
return
}
store := sessions.Default(ctx)
if store == nil {
ctx.JSON(200, gin.H{
"success": false,
"reauthenticate_required": true,
})
return
}
accessToken := store.Get("access_token").(string)
expiry := store.Get("expiry").(int64)
if expiry > (time.Now().UnixNano() / int64(time.Second)) {
res, err := discord.RefreshToken(store.Get("refresh_token").(string))
if err != nil { // Tell client to re-authenticate
ctx.JSON(200, gin.H{
"success": false,
"reauthenticate_required": true,
})
return
}
accessToken = res.AccessToken
store.Set("access_token", res.AccessToken)
store.Set("refresh_token", res.RefreshToken)
store.Set("expiry", (time.Now().UnixNano()/int64(time.Second))+int64(res.ExpiresIn))
store.Save()
}
if err := utils.LoadGuilds(accessToken, userId); err != nil {
ctx.JSON(500, utils.ErrorToResponse(err))
return
}
ctx.JSON(200, utils.SuccessResponse)
}

View File

@ -3,16 +3,12 @@ package root
import ( import (
"fmt" "fmt"
"github.com/TicketsBot/GoPanel/config" "github.com/TicketsBot/GoPanel/config"
dbclient "github.com/TicketsBot/GoPanel/database"
"github.com/TicketsBot/GoPanel/utils" "github.com/TicketsBot/GoPanel/utils"
"github.com/TicketsBot/GoPanel/utils/discord" "github.com/TicketsBot/GoPanel/utils/discord"
userEndpoint "github.com/TicketsBot/GoPanel/utils/discord/endpoints/user"
"github.com/TicketsBot/database"
"github.com/apex/log" "github.com/apex/log"
"github.com/gin-gonic/contrib/sessions" "github.com/gin-gonic/contrib/sessions"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/rxdn/gdl/objects/guild" "github.com/rxdn/gdl/rest"
"github.com/rxdn/gdl/objects/user"
"strings" "strings"
"time" "time"
) )
@ -65,8 +61,7 @@ func CallbackHandler(ctx *gin.Context) {
store.Set("expiry", (time.Now().UnixNano()/int64(time.Second))+int64(res.ExpiresIn)) store.Set("expiry", (time.Now().UnixNano()/int64(time.Second))+int64(res.ExpiresIn))
// Get ID + name // Get ID + name
var currentUser user.User currentUser, err := rest.GetCurrentUser(fmt.Sprintf("Bearer %s", res.AccessToken), nil)
err, _ = userEndpoint.CurrentUser.Request(store, nil, nil, &currentUser)
if err != nil { if err != nil {
ctx.String(500, err.Error()) ctx.String(500, err.Error())
return return
@ -79,37 +74,10 @@ func CallbackHandler(ctx *gin.Context) {
store.Set("avatar", currentUser.AvatarUrl(256)) store.Set("avatar", currentUser.AvatarUrl(256))
store.Save() store.Save()
var guilds []guild.Guild if err := utils.LoadGuilds(res.AccessToken, currentUser.Id); err == nil {
err, _ = userEndpoint.CurrentUserGuilds.Request(store, nil, nil, &guilds)
if err != nil {
log.Error(err.Error())
handleRedirect(ctx)
return
}
store.Set("has_guilds", true) store.Set("has_guilds", true)
store.Save() store.Save()
} else {
var wrappedGuilds []database.UserGuild
// endpoint's partial guild doesn't include ownerid
// we only user cached guilds on the index page, so it doesn't matter if we don't have have the real owner id
// if the user isn't the owner, as we pull from the cache on other endpoints
for _, guild := range guilds {
if guild.Owner {
guild.OwnerId = currentUser.Id
}
wrappedGuilds = append(wrappedGuilds, database.UserGuild{
GuildId: guild.Id,
Name: guild.Name,
Owner: guild.Owner,
UserPermissions: int32(guild.Permissions),
Icon: guild.Icon,
})
}
if err := dbclient.Client.UserGuilds.Set(currentUser.Id, wrappedGuilds); err != nil {
log.Error(err.Error()) log.Error(err.Error())
} }

View File

@ -128,6 +128,7 @@ func StartServer() {
userGroup := router.Group("/user", middleware.AuthenticateToken) userGroup := router.Group("/user", middleware.AuthenticateToken)
{ {
userGroup.GET("/guilds", api.GetGuilds) userGroup.GET("/guilds", api.GetGuilds)
userGroup.POST("/guilds/reload", api.ReloadGuildsHandler)
userGroup.GET("/permissionlevel", api.GetPermissionLevel) userGroup.GET("/permissionlevel", api.GetPermissionLevel)
{ {

4
go.mod
View File

@ -15,14 +15,14 @@ require (
github.com/gin-contrib/static v0.0.0-20191128031702-f81c604d8ac2 github.com/gin-contrib/static v0.0.0-20191128031702-f81c604d8ac2
github.com/gin-gonic/contrib v0.0.0-20191209060500-d6e26eeaa607 github.com/gin-gonic/contrib v0.0.0-20191209060500-d6e26eeaa607
github.com/gin-gonic/gin v1.6.2 github.com/gin-gonic/gin v1.6.2
github.com/go-redis/redis v6.15.8+incompatible github.com/go-redis/redis v6.15.9+incompatible
github.com/gofrs/uuid v3.3.0+incompatible github.com/gofrs/uuid v3.3.0+incompatible
github.com/gorilla/sessions v1.2.0 // indirect github.com/gorilla/sessions v1.2.0 // indirect
github.com/gorilla/websocket v1.4.2 github.com/gorilla/websocket v1.4.2
github.com/jackc/pgx/v4 v4.7.1 github.com/jackc/pgx/v4 v4.7.1
github.com/pasztorpisti/qs v0.0.0-20171216220353-8d6c33ee906c github.com/pasztorpisti/qs v0.0.0-20171216220353-8d6c33ee906c
github.com/pkg/errors v0.9.1 github.com/pkg/errors v0.9.1
github.com/rxdn/gdl v0.0.0-20201214225805-4ae598a98327 github.com/rxdn/gdl v0.0.0-20210105220812-1d2789cb2f0b
github.com/sirupsen/logrus v1.5.0 github.com/sirupsen/logrus v1.5.0
github.com/ulule/limiter/v3 v3.5.0 github.com/ulule/limiter/v3 v3.5.0
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a

View File

@ -208,12 +208,15 @@ html > ::-webkit-scrollbar {
} }
.guild { .guild {
width: 100%; display: flex;
align-items: center;
box-shadow: 0 4px 4px rgba(0, 0, 0, 0.25);
width: 33%;
background-color: #121212; background-color: #121212;
height: 100px; height: 100px;
margin-bottom: 10px; margin-bottom: 10px;
border-radius: 10px; border-radius: 10px;
display: flex;
cursor: pointer; cursor: pointer;
} }
@ -240,3 +243,23 @@ html > ::-webkit-scrollbar {
color: white !important; color: white !important;
padding-left: 10px; padding-left: 10px;
} }
.flex-container {
display: flex;
height: 100%;
width: 100%;
}
#guild-container {
display: flex;
flex-direction: row;
justify-content: space-between;
}
#refresh-container {
display: flex;
justify-content: center;
margin-top: 20px;
color: white;
}

View File

@ -36,3 +36,11 @@ function appendButton(tr, content, onclick) {
tdRemove.appendChild(removeButton); tdRemove.appendChild(removeButton);
tr.appendChild(tdRemove); tr.appendChild(tdRemove);
} }
function prependChild(parent, child) {
if (parent.children.length === 0) {
parent.appendChild(child);
} else {
parent.insertBefore(child, parent.children[0]);
}
}

View File

@ -9,22 +9,17 @@
</div> </div>
<div class="card-body" id="card"> <div class="card-body" id="card">
<div class="card-body table-responsive"> <div class="card-body table-responsive">
<p class="center-align white" style="padding-top: 50px; font-size: 16px; display: none" id="no-guilds"> <div class="flex-container" id="guild-container">
You are not the admin of any guilds that the bot is in. Click below to invite the bot: <div class="guild" onclick="invite();" id="invite-container">
<br/>
<a href="https://invite.ticketsbot.net"><button class="btn btn-primary btn-fill"><i class="fas fa-plus"></i> Invite</button></a>
</p>
<div class="container-fluid">
<div class="row" id="guild-container">
<div class="col-md-4">
<div class="guild align-items-center" onclick="invite();">
<i class="fas fa-plus fa-2x guild-icon-fa"></i> <i class="fas fa-plus fa-2x guild-icon-fa"></i>
<div class="align-items-center">
<span class="guild-name">Invite to your server</span> <span class="guild-name">Invite to your server</span>
</div> </div>
</div> </div>
</div>
</div> <div class="flex-container" id="refresh-container">
<button class="btn btn-primary btn-fill" onclick="refreshGuilds()">
<i class="fas fa-sync"></i> Refresh list
</button>
</div> </div>
</div> </div>
</div> </div>
@ -61,15 +56,11 @@
async function loadData() { async function loadData() {
const res = await axios.get('/user/guilds'); const res = await axios.get('/user/guilds');
if (res.data.length > 0) {
const container = document.getElementById('guild-container'); const container = document.getElementById('guild-container');
for (guild of res.data) { for (guild of res.data) {
const col = document.createElement('div');
col.classList.add('col-md-4');
const guildContainer = document.createElement('div'); const guildContainer = document.createElement('div');
guildContainer.classList.add('guild', 'align-items-center'); guildContainer.classList.add('guild');
if (guild.icon === undefined || guild.icon === null || guild.icon === "") { if (guild.icon === undefined || guild.icon === null || guild.icon === "") {
const icon = document.createElement('i'); const icon = document.createElement('i');
@ -89,7 +80,6 @@
} }
const nameContainer = document.createElement('div'); const nameContainer = document.createElement('div');
nameContainer.classList.add('align-items-center');
const name = document.createElement('span'); const name = document.createElement('span');
name.classList.add('guild-name'); name.classList.add('guild-name');
@ -99,14 +89,33 @@
guildContainer.appendChild(nameContainer); guildContainer.appendChild(nameContainer);
const guildId = guild.id const guildId = guild.id
guildContainer.onclick = async () => { await goto(guildId) }; guildContainer.onclick = async () => {
await goto(guildId)
};
col.appendChild(guildContainer); container.insertBefore(guildContainer, container.children[container.children.length - 1]);
container.appendChild(col);
} }
} else {
document.getElementById('no-guilds').style.display = 'block';
} }
async function refreshGuilds() {
await withLoadingScreen(async () => {
const res = await axios.post('/user/guilds/reload');
if (res.status !== 200) {
notifyError(res.data.error);
return;
}
if (!res.data.success && res.data['reauthenticate_required'] === true) {
window.location.href = "/login";
return;
}
const inviteContainer = document.getElementById('invite-container');
document.getElementById('guild-container').innerHTML = ``;
document.getElementById('guild-container').appendChild(inviteContainer);
await loadData();
});
} }
withLoadingScreen(async () => { withLoadingScreen(async () => {

View File

@ -20,7 +20,6 @@
<tr> <tr>
<th>Channel</th> <th>Channel</th>
<th>Panel Title</th> <th>Panel Title</th>
<th>Panel Content</th>
<th>Ticket Channel Category</th> <th>Ticket Channel Category</th>
<th>Edit</th> <th>Edit</th>
<th>Delete</th> <th>Delete</th>
@ -377,7 +376,6 @@
appendTd(tr, `#${getChannelName(channels, panel.channel_id)}`); appendTd(tr, `#${getChannelName(channels, panel.channel_id)}`);
appendTd(tr, panel.title); appendTd(tr, panel.title);
appendTd(tr, panel.content);
appendTd(tr, getChannelName(channels, panel.category_id)); appendTd(tr, getChannelName(channels, panel.category_id));
// build edit button // build edit button

38
utils/guildutils.go Normal file
View File

@ -0,0 +1,38 @@
package utils
import (
"fmt"
dbclient "github.com/TicketsBot/GoPanel/database"
"github.com/TicketsBot/database"
"github.com/rxdn/gdl/rest"
)
func LoadGuilds(accessToken string, userId uint64) error {
authHeader := fmt.Sprintf("Bearer %s", accessToken)
data := rest.CurrentUserGuildsData{
Limit: 100,
}
guilds, err := rest.GetCurrentUserGuilds(authHeader, nil, data)
if err != nil {
return err
}
var wrappedGuilds []database.UserGuild
// endpoint's partial guild doesn't include ownerid
// we only user cached guilds on the index page, so it doesn't matter if we don't have have the real owner id
// if the user isn't the owner, as we pull from the cache on other endpoints
for _, guild := range guilds {
wrappedGuilds = append(wrappedGuilds, database.UserGuild{
GuildId: guild.Id,
Name: guild.Name,
Owner: guild.Owner,
UserPermissions: int32(guild.Permissions),
Icon: guild.Icon,
})
}
return dbclient.Client.UserGuilds.Set(userId, wrappedGuilds)
}

View File

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