Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 24 additions & 2 deletions backend/app/http/endpoints/api/admin/serverblacklist/list.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,21 @@
package serverblacklist

import (
"net/http"
"strconv"

"github.com/gin-gonic/gin"
"github.com/ticketsbot-cloud/dashboard/backend/app"
"github.com/ticketsbot-cloud/dashboard/backend/database"
"github.com/ticketsbot-cloud/dashboard/backend/utils"
"github.com/ticketsbot-cloud/dashboard/backend/log"
"github.com/ticketsbot-cloud/dashboard/backend/rpc/cache"
"go.uber.org/zap"
)

type entryData struct {
GuildId string `json:"guild_id"`
Name string `json:"name,omitempty"`
Icon string `json:"icon,omitempty"`
Reason *string `json:"reason"`
OwnerId *string `json:"owner_id"`
RealOwnerId *string `json:"real_owner_id"`
Expand All @@ -18,17 +24,33 @@ type entryData struct {
func ListHandler(ctx *gin.Context) {
entries, err := database.Client.ServerBlacklist.ListAllEntries(ctx)
if err != nil {
ctx.JSON(500, utils.ErrorStr("Failed to fetch records. Please try again."))
_ = ctx.AbortWithError(http.StatusInternalServerError, app.NewError(err, "Failed to fetch records. Please try again."))
return
}

guildIds := make([]uint64, len(entries))
for i, entry := range entries {
guildIds[i] = entry.GuildId
}

// AdminLayout's access check calls this route, so a cache failure must not 500 it
guilds, err := cache.Instance.GetGuilds(ctx, guildIds)
if err != nil {
log.Logger.Warn("Failed to resolve blacklisted servers", zap.Error(err))
}

result := make([]entryData, len(entries))
for i, entry := range entries {
data := entryData{
GuildId: strconv.FormatUint(entry.GuildId, 10),
Reason: entry.Reason,
}

if guild, ok := guilds[entry.GuildId]; ok {
data.Name = guild.Name
data.Icon = guild.Icon
}

if entry.OwnerId != nil {
s := strconv.FormatUint(*entry.OwnerId, 10)
data.OwnerId = &s
Expand Down
80 changes: 80 additions & 0 deletions backend/app/http/endpoints/api/admin/submissionblacklist/add.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package submissionblacklist

import (
"fmt"
"net/http"
"strconv"
"strings"

dbmodel "github.com/TicketsBot-cloud/database"
"github.com/gin-gonic/gin"
"github.com/ticketsbot-cloud/dashboard/backend/app"
"github.com/ticketsbot-cloud/dashboard/backend/app/http/audit"
"github.com/ticketsbot-cloud/dashboard/backend/database"
"github.com/ticketsbot-cloud/dashboard/backend/utils"
)

type addBody struct {
Reason *string `json:"reason"`
}

func AddHandler(feature dbmodel.SubmissionFeature) gin.HandlerFunc {
return func(ctx *gin.Context) {
authUserId := ctx.Keys["userid"].(uint64)

targetType, targetId, ok := parseTarget(ctx, feature)
if !ok {
return
}

var body addBody
_ = ctx.ShouldBindJSON(&body)

var reason *string
if body.Reason != nil {
if trimmed := strings.TrimSpace(*body.Reason); trimmed != "" {
reason = &trimmed
}
}

if err := database.Client.SubmissionBlacklist.Add(ctx, feature, targetType, targetId, reason); err != nil {
_ = ctx.AbortWithError(http.StatusInternalServerError, app.NewError(err, "Failed to process request. Please try again."))
return
}

audit.LogStaff(audit.LogEntry{
UserId: authUserId,
ActionType: dbmodel.AuditActionSubmissionBlacklistAdd,
ResourceType: dbmodel.AuditResourceSubmissionBlacklist,
ResourceId: audit.StringPtr(auditResourceId(feature, targetType, targetId)),
NewData: map[string]any{
"feature": feature,
"target_type": targetType,
"target_id": strconv.FormatUint(targetId, 10),
"reason": reason,
},
})
ctx.Status(http.StatusNoContent)
}
}

func parseTarget(ctx *gin.Context, feature dbmodel.SubmissionFeature) (dbmodel.SubmissionTargetType, uint64, bool) {
targetType := dbmodel.SubmissionTargetType(ctx.Param("type"))
if !feature.AllowsTarget(targetType) {
ctx.JSON(http.StatusBadRequest, utils.ErrorStr("Invalid target type"))
return "", 0, false
}

// int8 column: an ID above MaxInt64 must be a 400, not a pgx 500
targetId, err := strconv.ParseInt(ctx.Param("targetid"), 10, 64)
if err != nil || targetId <= 0 {
ctx.JSON(http.StatusBadRequest, utils.ErrorStr("Invalid target ID"))
return "", 0, false
}

return targetType, uint64(targetId), true
}

func auditResourceId(feature dbmodel.SubmissionFeature, targetType dbmodel.SubmissionTargetType, targetId uint64) string {
return fmt.Sprintf("%s:%s:%d", feature, targetType, targetId)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package submissionblacklist

import (
"net/http"

dbmodel "github.com/TicketsBot-cloud/database"
"github.com/gin-gonic/gin"
"github.com/ticketsbot-cloud/dashboard/backend/app"
"github.com/ticketsbot-cloud/dashboard/backend/database"
"github.com/ticketsbot-cloud/dashboard/backend/rpc/cache"
)

type entryData struct {
TargetType dbmodel.SubmissionTargetType `json:"target_type"`
TargetId uint64 `json:"target_id,string"`
Name string `json:"name,omitempty"`
AvatarUrl string `json:"avatar_url,omitempty"`
Icon string `json:"icon,omitempty"`
Reason *string `json:"reason"`
}

func ListHandler(feature dbmodel.SubmissionFeature) gin.HandlerFunc {
return func(ctx *gin.Context) {
entries, err := database.Client.SubmissionBlacklist.List(ctx, feature)
if err != nil {
_ = ctx.AbortWithError(http.StatusInternalServerError, app.NewError(err, "Failed to fetch records. Please try again."))
return
}

userIds := make([]uint64, 0, len(entries))
guildIds := make([]uint64, 0, len(entries))
for _, entry := range entries {
switch entry.TargetType {
case dbmodel.SubmissionTargetUser:
userIds = append(userIds, entry.TargetId)
case dbmodel.SubmissionTargetGuild:
guildIds = append(guildIds, entry.TargetId)
}
}

users, err := cache.Instance.GetUsers(ctx, userIds)
if err != nil {
_ = ctx.AbortWithError(http.StatusInternalServerError, app.NewError(err, "Failed to resolve blacklisted users"))
return
}

guilds, err := cache.Instance.GetGuilds(ctx, guildIds)
if err != nil {
_ = ctx.AbortWithError(http.StatusInternalServerError, app.NewError(err, "Failed to resolve blacklisted servers"))
return
}

result := make([]entryData, len(entries))
for i, entry := range entries {
result[i] = entryData{
TargetType: entry.TargetType,
TargetId: entry.TargetId,
Reason: entry.Reason,
}

switch entry.TargetType {
case dbmodel.SubmissionTargetUser:
if user, ok := users[entry.TargetId]; ok {
result[i].Name = user.Username
result[i].AvatarUrl = user.AvatarUrl(256)
}
case dbmodel.SubmissionTargetGuild:
if guild, ok := guilds[entry.TargetId]; ok {
result[i].Name = guild.Name
result[i].Icon = guild.Icon
}
}
}

ctx.JSON(http.StatusOK, result)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package submissionblacklist

import (
"net/http"
"strconv"

dbmodel "github.com/TicketsBot-cloud/database"
"github.com/gin-gonic/gin"
"github.com/ticketsbot-cloud/dashboard/backend/app"
"github.com/ticketsbot-cloud/dashboard/backend/app/http/audit"
"github.com/ticketsbot-cloud/dashboard/backend/database"
)

func RemoveHandler(feature dbmodel.SubmissionFeature) gin.HandlerFunc {
return func(ctx *gin.Context) {
authUserId := ctx.Keys["userid"].(uint64)

targetType, targetId, ok := parseTarget(ctx, feature)
if !ok {
return
}

if err := database.Client.SubmissionBlacklist.Delete(ctx, feature, targetType, targetId); err != nil {
_ = ctx.AbortWithError(http.StatusInternalServerError, app.NewError(err, "Failed to delete record. Please try again."))
return
}

audit.LogStaff(audit.LogEntry{
UserId: authUserId,
ActionType: dbmodel.AuditActionSubmissionBlacklistRemove,
ResourceType: dbmodel.AuditResourceSubmissionBlacklist,
ResourceId: audit.StringPtr(auditResourceId(feature, targetType, targetId)),
OldData: map[string]any{
"feature": feature,
"target_type": targetType,
"target_id": strconv.FormatUint(targetId, 10),
},
})
ctx.Status(http.StatusNoContent)
}
}
4 changes: 4 additions & 0 deletions backend/app/http/endpoints/api/gallery/resubmit.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ func ResubmitHandler(ctx *gin.Context) {
guildId := ctx.Keys["guildid"].(uint64)
userId := ctx.Keys["userid"].(uint64)

if utils.RejectIfSubmissionBlacklisted(ctx, database.SubmissionFeatureGallery, userId, guildId) {
return
}

listingId, err := strconv.Atoi(ctx.Param("listingId"))
if err != nil {
ctx.JSON(http.StatusBadRequest, utils.ErrorStr("Invalid listing ID"))
Expand Down
4 changes: 4 additions & 0 deletions backend/app/http/endpoints/api/gallery/submit.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ func SubmitHandler(ctx *gin.Context) {
guildId := ctx.Keys["guildid"].(uint64)
userId := ctx.Keys["userid"].(uint64)

if utils.RejectIfSubmissionBlacklisted(ctx, database.SubmissionFeatureGallery, userId, guildId) {
return
}

panelIdStr := ctx.Param("panelid")
panelId, err := strconv.Atoi(panelIdStr)
if err != nil {
Expand Down
4 changes: 4 additions & 0 deletions backend/app/http/endpoints/api/gallery/submit_form.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ func SubmitFormHandler(ctx *gin.Context) {
guildId := ctx.Keys["guildid"].(uint64)
userId := ctx.Keys["userid"].(uint64)

if utils.RejectIfSubmissionBlacklisted(ctx, database.SubmissionFeatureGallery, userId, guildId) {
return
}

formIdStr := ctx.Param("formid")
formId, err := strconv.Atoi(formIdStr)
if err != nil {
Expand Down
4 changes: 4 additions & 0 deletions backend/app/http/endpoints/api/gallery/submit_tag.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ func SubmitTagHandler(ctx *gin.Context) {
guildId := ctx.Keys["guildid"].(uint64)
userId := ctx.Keys["userid"].(uint64)

if utils.RejectIfSubmissionBlacklisted(ctx, database.SubmissionFeatureGallery, userId, guildId) {
return
}

tagId := ctx.Param("tagid")

var body submitBody
Expand Down
4 changes: 4 additions & 0 deletions backend/app/http/endpoints/api/integrations/setpublic.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ func SetIntegrationPublicHandler(ctx *gin.Context) {
return
}

if utils.RejectIfSubmissionBlacklisted(ctx, dbmodel.SubmissionFeatureIntegrations, userId, 0) {
return
}

e := embed.NewEmbed().
SetTitle("Public Integration Request").
SetColor(0xfcb97d).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,10 @@ func UpdateIntegrationHandler(ctx *gin.Context) {
return
}

if integration.Public && utils.RejectIfSubmissionBlacklisted(ctx, database.SubmissionFeatureIntegrations, userId, 0) {
return
}

if data.ValidationUrl != nil {
sameHost, err := isSameValidationUrlHost(data.WebhookUrl, *data.ValidationUrl)
if err != nil {
Expand Down
21 changes: 15 additions & 6 deletions backend/app/http/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"time"

"github.com/TicketsBot-cloud/common/permission"
dbmodel "github.com/TicketsBot-cloud/database"
sentrygin "github.com/getsentry/sentry-go/gin"
"github.com/gin-gonic/gin"
"github.com/penglongli/gin-metrics/ginmetrics"
Expand All @@ -21,6 +22,7 @@ import (
admin_premiumkeys "github.com/ticketsbot-cloud/dashboard/backend/app/http/endpoints/api/admin/premiumkeys"
admin_serverblacklist "github.com/ticketsbot-cloud/dashboard/backend/app/http/endpoints/api/admin/serverblacklist"
admin_skus "github.com/ticketsbot-cloud/dashboard/backend/app/http/endpoints/api/admin/skus"
admin_submissionblacklist "github.com/ticketsbot-cloud/dashboard/backend/app/http/endpoints/api/admin/submissionblacklist"
admin_utilities "github.com/ticketsbot-cloud/dashboard/backend/app/http/endpoints/api/admin/utilities"
api_affiliate "github.com/ticketsbot-cloud/dashboard/backend/app/http/endpoints/api/affiliate"
api_analytics "github.com/ticketsbot-cloud/dashboard/backend/app/http/endpoints/api/analytics"
Expand Down Expand Up @@ -489,13 +491,9 @@ func StartServer(logger *zap.Logger, sm *livechat.SocketManager) *nethttp.Server
adminTier.DELETE("/gallery/:id", admin_gallery.RemoveHandler)

adminTier.POST("/server-blacklist/:guildid", admin_serverblacklist.AddHandler)
adminTier.DELETE("/server-blacklist/:guildid", admin_serverblacklist.RemoveHandler)

adminTier.GET("/integrations", admin_integrations.ListIntegrationsHandler)
adminTier.GET("/integrations/:integrationid", admin_integrations.GetIntegrationDetailHandler)
adminTier.POST("/integrations/:integrationid/approve", admin_integrations.ApproveIntegrationHandler)
adminTier.POST("/integrations/:integrationid/reject", admin_integrations.RejectIntegrationHandler)
adminTier.POST("/integrations/:integrationid/unapprove", admin_integrations.UnapproveIntegrationHandler)
adminTier.GET("/submission-blacklist/gallery", admin_submissionblacklist.ListHandler(dbmodel.SubmissionFeatureGallery))
adminTier.POST("/submission-blacklist/gallery/:type/:targetid", admin_submissionblacklist.AddHandler(dbmodel.SubmissionFeatureGallery))

adminTier.GET("/skus", admin_skus.ListHandler)

Expand All @@ -522,6 +520,11 @@ func StartServer(logger *zap.Logger, sm *livechat.SocketManager) *nethttp.Server
ownerTier.GET("/global-blacklist", admin_globalblacklist.ListHandler)
ownerTier.POST("/global-blacklist/:userid", admin_globalblacklist.AddHandler)
ownerTier.DELETE("/global-blacklist/:userid", admin_globalblacklist.RemoveHandler)
ownerTier.DELETE("/server-blacklist/:guildid", admin_serverblacklist.RemoveHandler)
ownerTier.DELETE("/submission-blacklist/gallery/:type/:targetid", admin_submissionblacklist.RemoveHandler(dbmodel.SubmissionFeatureGallery))
ownerTier.GET("/submission-blacklist/integrations", admin_submissionblacklist.ListHandler(dbmodel.SubmissionFeatureIntegrations))
ownerTier.POST("/submission-blacklist/integrations/:type/:targetid", admin_submissionblacklist.AddHandler(dbmodel.SubmissionFeatureIntegrations))
ownerTier.DELETE("/submission-blacklist/integrations/:type/:targetid", admin_submissionblacklist.RemoveHandler(dbmodel.SubmissionFeatureIntegrations))
ownerTier.POST("/skus", admin_skus.CreateHandler)
ownerTier.PUT("/skus/:skuid", admin_skus.UpdateHandler)
ownerTier.DELETE("/skus/:skuid", admin_skus.DeleteHandler)
Expand All @@ -531,6 +534,12 @@ func StartServer(logger *zap.Logger, sm *livechat.SocketManager) *nethttp.Server
ownerTier.PUT("/polar-products/:productid", admin_polarproducts.UpdateHandler)
ownerTier.DELETE("/polar-products/:productid", admin_polarproducts.DeleteHandler)

ownerTier.GET("/integrations", admin_integrations.ListIntegrationsHandler)
ownerTier.GET("/integrations/:integrationid", admin_integrations.GetIntegrationDetailHandler)
ownerTier.POST("/integrations/:integrationid/approve", admin_integrations.ApproveIntegrationHandler)
ownerTier.POST("/integrations/:integrationid/reject", admin_integrations.RejectIntegrationHandler)
ownerTier.POST("/integrations/:integrationid/unapprove", admin_integrations.UnapproveIntegrationHandler)

ownerTier.POST("/affiliate", admin_affiliate.CreateHandler)
ownerTier.POST("/affiliate/:id/approve", admin_affiliate.ApproveHandler)
ownerTier.POST("/affiliate/:id/revoke", admin_affiliate.RevokeHandler)
Expand Down
4 changes: 2 additions & 2 deletions backend/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ replace github.com/go-playground/validator/v10 => github.com/go-playground/valid
require (
github.com/BurntSushi/toml v1.2.1
github.com/TicketsBot-cloud/archiverclient v0.0.0-20251015181023-f0b66a074704
github.com/TicketsBot-cloud/common v0.0.0-20260905165836-38e4090764a4
github.com/TicketsBot-cloud/database v0.0.0-20260913165941-c2ecc7191b02
github.com/TicketsBot-cloud/common v0.0.0-20260927072029-424264fc4be2
github.com/TicketsBot-cloud/database v0.0.0-20260927071905-7a22c7be0e4a
github.com/TicketsBot-cloud/gdl v0.0.0-20260612070331-a3947b410d3e
github.com/TicketsBot-cloud/logarchiver v0.0.0-20251018211319-7a7df5cacbdc
github.com/TicketsBot-cloud/worker v0.0.0-20260827073646-455b39e53841
Expand Down
Loading
Loading