Tag management

This commit is contained in:
Dot-Rar 2020-04-26 14:27:45 +01:00
parent 7328719629
commit f4e484357f
11 changed files with 313 additions and 0 deletions

View File

@ -0,0 +1,59 @@
package api
import (
"github.com/TicketsBot/GoPanel/database/table"
"github.com/gin-gonic/gin"
)
type tag struct {
Id string `json:"id"`
Content string `json:"content"`
}
func CreateTag(ctx *gin.Context) {
guildId := ctx.Keys["guildid"].(uint64)
var data tag
if err := ctx.BindJSON(&data); err != nil {
ctx.AbortWithStatusJSON(400, gin.H{
"success": false,
"error": err.Error(),
})
return
}
if !data.verifyIdLength() {
ctx.AbortWithStatusJSON(400, gin.H{
"success": false,
"error": "Tag ID must be 1 - 16 characters in length",
})
return
}
if !data.verifyContentLength() {
ctx.AbortWithStatusJSON(400, gin.H{
"success": false,
"error": "Tag content must be 1 - 2000 characters in length",
})
return
}
if err := table.AddTag(guildId, data.Id, data.Content); err != nil {
ctx.AbortWithStatusJSON(500, gin.H{
"success": false,
"error": err.Error(),
})
} else {
ctx.JSON(200, gin.H{
"success": true,
})
}
}
func (t *tag) verifyIdLength() bool {
return len(t.Id) > 0 && len(t.Id) <= 16
}
func (t *tag) verifyContentLength() bool {
return len(t.Content) > 0 && len(t.Content) <= 2000
}

View File

@ -0,0 +1,30 @@
package api
import (
"github.com/TicketsBot/GoPanel/database/table"
"github.com/gin-gonic/gin"
)
func DeleteTag(ctx *gin.Context) {
guildId := ctx.Keys["guildid"].(uint64)
tagId := ctx.Param("tag")
if tagId == "" || len(tagId) > 16 {
ctx.JSON(400, gin.H{
"success": false,
"error": "Invalid tag",
})
return
}
if err := table.DeleteTag(guildId, tagId); err != nil {
ctx.JSON(500, gin.H{
"success": false,
"error": err.Error(),
})
} else {
ctx.JSON(200, gin.H{
"success": true,
})
}
}

View File

@ -0,0 +1,23 @@
package api
import (
"github.com/TicketsBot/GoPanel/database/table"
"github.com/gin-gonic/gin"
)
func TagsListHandler(ctx *gin.Context) {
guildId := ctx.Keys["guildid"].(uint64)
wrapped := make([]tag, 0)
tags := make(chan []table.Tag)
go table.GetTags(guildId, tags)
for _, t := range <-tags {
wrapped = append(wrapped, tag{
Id: t.Id,
Content: t.Content,
})
}
ctx.JSON(200, wrapped)
}

View File

@ -0,0 +1,19 @@
package manage
import (
"github.com/TicketsBot/GoPanel/config"
"github.com/gin-gonic/contrib/sessions"
"github.com/gin-gonic/gin"
)
func TagsHandler(ctx *gin.Context) {
store := sessions.Default(ctx)
guildId := ctx.Keys["guildid"].(uint64)
ctx.HTML(200, "manage/tags", gin.H{
"name": store.Get("name").(string),
"guildId": guildId,
"avatar": store.Get("avatar").(string),
"baseUrl": config.Conf.Server.BaseUrl,
})
}

View File

@ -63,6 +63,7 @@ func StartServer() {
authenticateGuild.GET("/manage/:id/logs/modmail", manage.ModmailLogsHandler)
authenticateGuild.GET("/manage/:id/blacklist", manage.BlacklistHandler)
authenticateGuild.GET("/manage/:id/panels", manage.PanelHandler)
authenticateGuild.GET("/manage/:id/tags", manage.TagsHandler)
authenticateGuild.GET("/manage/:id/tickets", manage.TicketListHandler)
authenticateGuild.GET("/manage/:id/tickets/view/:uuid", manage.TicketViewHandler)
@ -96,6 +97,10 @@ func StartServer() {
guildAuthApi.GET("/:id/tickets/:uuid", api.GetTicket)
guildAuthApi.POST("/:id/tickets/:uuid", api.SendMessage)
guildAuthApi.DELETE("/:id/tickets/:uuid", api.CloseTicket)
guildAuthApi.GET("/:id/tags", api.TagsListHandler)
guildAuthApi.PUT("/:id/tags", api.CreateTag)
guildAuthApi.DELETE("/:id/tags/:tag", api.DeleteTag)
}
userGroup := router.Group("/user", middleware.AuthenticateToken)
@ -120,6 +125,7 @@ func createRenderer() multitemplate.Renderer {
r = addManageTemplate(r, "ticketlist")
r = addManageTemplate(r, "ticketview")
r = addManageTemplate(r, "panels")
r = addManageTemplate(r, "tags")
return r
}

42
database/table/tags.go Normal file
View File

@ -0,0 +1,42 @@
package table
import (
"github.com/TicketsBot/GoPanel/database"
uuid "github.com/satori/go.uuid"
)
type Tag struct {
Uuid string `gorm:"column:UUID;type:varchar(36);unique;primary_key"`
Id string `gorm:"column:ID;type:varchar(16)"`
Guild uint64 `gorm:"column:GUILDID"`
Content string `gorm:"column:TEXT;type:TEXT"`
}
func (Tag) TableName() string {
return "cannedresponses"
}
func GetTag(guild uint64, id string, ch chan string) {
var node Tag
database.Database.Where(Tag{Id: id, Guild: guild}).Take(&node)
ch <- node.Content
}
func GetTags(guild uint64, ch chan []Tag) {
var rows []Tag
database.Database.Where(Tag{Guild: guild}).Find(&rows)
ch <- rows
}
func AddTag(guild uint64, id string, content string) error {
return database.Database.Create(&Tag{
Uuid: uuid.NewV4().String(),
Id: id,
Guild: guild,
Content: content,
}).Error
}
func DeleteTag(guild uint64, id string) error {
return database.Database.Where(Tag{Id: id, Guild: guild}).Delete(&Tag{}).Error
}

1
go.mod
View File

@ -22,6 +22,7 @@ require (
github.com/pkg/errors v0.9.1
github.com/robfig/go-cache v0.0.0-20130306151617-9fc39e0dbf62
github.com/rxdn/gdl v0.0.0-20200421193445-f200b9f466d7
github.com/satori/go.uuid v1.2.0
github.com/ulule/limiter/v3 v3.5.0
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a
)

View File

@ -111,3 +111,9 @@ body {
#premium-ad {
display: none;
}
.tag-content {
min-width: 300px;
max-width: 300px;
word-wrap: break-word;
}

View File

@ -122,6 +122,7 @@
td.appendChild(document.createTextNode(content));
td.classList.add('white');
tr.appendChild(td);
return td
}
function appendButton(tr, content, onclick) {

View File

@ -19,6 +19,9 @@
<li class="nav-item">
<a class="nav-link" href="/manage/{{.guildId}}/panels"><i class="fas fa-mouse-pointer icon"></i>Reaction Panels</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/manage/{{.guildId}}/tags"><i class="fas fa-tags icon"></i>Tags</a>
</li>
</ul>
<ul class="navbar-nav navbar-right">
<li class="nav-item">

View File

@ -0,0 +1,123 @@
{{define "content"}}
<div class="content">
<div class="container-fluid">
<div class="row">
<div class="col-md-12">
<div class="card">
<div class="card-header">
<h4 class="card-title">Tags</h4>
</div>
<div class="card-body">
<table class="table table-hover table-striped">
<thead>
<tr>
<th>ID</th>
<th>Content</th>
<th>Delete</th>
</tr>
</thead>
<tbody id="tag-container">
</tbody>
</table>
</div>
</div>
<div class="card">
<div class="card-header">
<h4 class="card-title">Create A Tag</h4>
</div>
<div class="card-body">
<form onsubmit="createTag(); return false;">
<div class="row">
<div class="col-md-4 pr-1">
<div class="form-group">
<label class="black">Tag ID</label>
<input name="title" type="text" class="form-control" placeholder="My Tag" id="id">
</div>
</div>
<div class="col-md-8 pr-1">
<div class="form-group">
<label class="black">Tag Response</label>
<textarea name="content" type="text" class="form-control"
placeholder="Response to the tag" id="content"></textarea>
</div>
</div>
</div>
<div class="row">
<div class="col-md-2 pr-1 offset-md-5">
<div class="form-group">
<button type="submit" class="btn btn-primary btn-fill"><i class="fas fa-paper-plane"></i> Submit</button>
</div>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
<div aria-live="polite" aria-atomic="true" style="position: relative; min-height: 200px;">
<div style="position: absolute; right: 10px" id="toast-container">
</div>
</div>
<script>
async function createTag() {
const id = document.getElementById('id').value;
const content = document.getElementById('content').value;
const data = {
id: id,
content: content
};
const res = await axios.put('/api/{{.guildId}}/tags', data);
if (res.status === 200 && res.data.success) {
document.getElementById('id').value = '';
document.getElementById('content').value = '';
appendTag(data);
} else {
showToast('Error', res.data.error);
}
}
async function deleteTag(id) {
const res = await axios.delete('/api/{{.guildId}}/tags/' + id);
if (res.status === 200 && res.data.success) {
const tr = document.getElementById(id);
tr.parentNode.removeChild(tr);
} else {
showToast('Error', res.data.error);
}
}
function appendTag(tag) {
const container = document.getElementById('tag-container');
const tr = document.createElement('tr');
tr.id = tag.id;
appendTd(tr, tag.id);
appendTd(tr, tag.content).classList.add('tag-content');
appendButton(tr, 'Delete', () => { deleteTag(tag.id); });
container.appendChild(tr);
}
async function loadData() {
const res = await axios.get('/api/{{.guildId}}/tags');
if (res.status === 200) {
for (tag of res.data) {
appendTag(tag);
}
}
}
loadData();
</script>
</div>
{{end}}