Block requests without known hosts for security reasons.
We now track stats for Uptimebot. We now track stats for malformed requests. Added json as a valid column type. Continued work on poll posts. Added the polls table. Added the polls_voters table. Added the poll column to the topics table. Changed the type of the poll column on the replies table from boolean to int.
This commit is contained in:
parent
0ce7f8a9d7
commit
59754b489d
|
@ -0,0 +1,202 @@
|
||||||
|
package common
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
|
)
|
||||||
|
|
||||||
|
type PollCache interface {
|
||||||
|
Get(id int) (*Poll, error)
|
||||||
|
GetUnsafe(id int) (*Poll, error)
|
||||||
|
BulkGet(ids []int) (list []*Poll)
|
||||||
|
Set(item *Poll) error
|
||||||
|
Add(item *Poll) error
|
||||||
|
AddUnsafe(item *Poll) error
|
||||||
|
Remove(id int) error
|
||||||
|
RemoveUnsafe(id int) error
|
||||||
|
Flush()
|
||||||
|
Length() int
|
||||||
|
SetCapacity(capacity int)
|
||||||
|
GetCapacity() int
|
||||||
|
}
|
||||||
|
|
||||||
|
type MemoryPollCache struct {
|
||||||
|
items map[int]*Poll
|
||||||
|
length int64
|
||||||
|
capacity int
|
||||||
|
|
||||||
|
sync.RWMutex
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMemoryPollCache gives you a new instance of MemoryPollCache
|
||||||
|
func NewMemoryPollCache(capacity int) *MemoryPollCache {
|
||||||
|
return &MemoryPollCache{
|
||||||
|
items: make(map[int]*Poll),
|
||||||
|
capacity: capacity,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mus *MemoryPollCache) Get(id int) (*Poll, error) {
|
||||||
|
mus.RLock()
|
||||||
|
item, ok := mus.items[id]
|
||||||
|
mus.RUnlock()
|
||||||
|
if ok {
|
||||||
|
return item, nil
|
||||||
|
}
|
||||||
|
return item, ErrNoRows
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mus *MemoryPollCache) BulkGet(ids []int) (list []*Poll) {
|
||||||
|
list = make([]*Poll, len(ids))
|
||||||
|
mus.RLock()
|
||||||
|
for i, id := range ids {
|
||||||
|
list[i] = mus.items[id]
|
||||||
|
}
|
||||||
|
mus.RUnlock()
|
||||||
|
return list
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mus *MemoryPollCache) GetUnsafe(id int) (*Poll, error) {
|
||||||
|
item, ok := mus.items[id]
|
||||||
|
if ok {
|
||||||
|
return item, nil
|
||||||
|
}
|
||||||
|
return item, ErrNoRows
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mus *MemoryPollCache) Set(item *Poll) error {
|
||||||
|
mus.Lock()
|
||||||
|
user, ok := mus.items[item.ID]
|
||||||
|
if ok {
|
||||||
|
mus.Unlock()
|
||||||
|
*user = *item
|
||||||
|
} else if int(mus.length) >= mus.capacity {
|
||||||
|
mus.Unlock()
|
||||||
|
return ErrStoreCapacityOverflow
|
||||||
|
} else {
|
||||||
|
mus.items[item.ID] = item
|
||||||
|
mus.Unlock()
|
||||||
|
atomic.AddInt64(&mus.length, 1)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mus *MemoryPollCache) Add(item *Poll) error {
|
||||||
|
if int(mus.length) >= mus.capacity {
|
||||||
|
return ErrStoreCapacityOverflow
|
||||||
|
}
|
||||||
|
mus.Lock()
|
||||||
|
mus.items[item.ID] = item
|
||||||
|
mus.length = int64(len(mus.items))
|
||||||
|
mus.Unlock()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mus *MemoryPollCache) AddUnsafe(item *Poll) error {
|
||||||
|
if int(mus.length) >= mus.capacity {
|
||||||
|
return ErrStoreCapacityOverflow
|
||||||
|
}
|
||||||
|
mus.items[item.ID] = item
|
||||||
|
mus.length = int64(len(mus.items))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mus *MemoryPollCache) Remove(id int) error {
|
||||||
|
mus.Lock()
|
||||||
|
_, ok := mus.items[id]
|
||||||
|
if !ok {
|
||||||
|
mus.Unlock()
|
||||||
|
return ErrNoRows
|
||||||
|
}
|
||||||
|
delete(mus.items, id)
|
||||||
|
mus.Unlock()
|
||||||
|
atomic.AddInt64(&mus.length, -1)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mus *MemoryPollCache) RemoveUnsafe(id int) error {
|
||||||
|
_, ok := mus.items[id]
|
||||||
|
if !ok {
|
||||||
|
return ErrNoRows
|
||||||
|
}
|
||||||
|
delete(mus.items, id)
|
||||||
|
atomic.AddInt64(&mus.length, -1)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mus *MemoryPollCache) Flush() {
|
||||||
|
mus.Lock()
|
||||||
|
mus.items = make(map[int]*Poll)
|
||||||
|
mus.length = 0
|
||||||
|
mus.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ! Is this concurrent?
|
||||||
|
// Length returns the number of users in the memory cache
|
||||||
|
func (mus *MemoryPollCache) Length() int {
|
||||||
|
return int(mus.length)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mus *MemoryPollCache) SetCapacity(capacity int) {
|
||||||
|
mus.capacity = capacity
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mus *MemoryPollCache) GetCapacity() int {
|
||||||
|
return mus.capacity
|
||||||
|
}
|
||||||
|
|
||||||
|
type NullPollCache struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewNullPollCache gives you a new instance of NullPollCache
|
||||||
|
func NewNullPollCache() *NullPollCache {
|
||||||
|
return &NullPollCache{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mus *NullPollCache) Get(id int) (*Poll, error) {
|
||||||
|
return nil, ErrNoRows
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mus *NullPollCache) BulkGet(ids []int) (list []*Poll) {
|
||||||
|
return list
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mus *NullPollCache) GetUnsafe(id int) (*Poll, error) {
|
||||||
|
return nil, ErrNoRows
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mus *NullPollCache) Set(_ *Poll) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mus *NullPollCache) Add(item *Poll) error {
|
||||||
|
_ = item
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mus *NullPollCache) AddUnsafe(item *Poll) error {
|
||||||
|
_ = item
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mus *NullPollCache) Remove(id int) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mus *NullPollCache) RemoveUnsafe(id int) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mus *NullPollCache) Flush() {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mus *NullPollCache) Length() int {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mus *NullPollCache) SetCapacity(_ int) {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mus *NullPollCache) GetCapacity() int {
|
||||||
|
return 0
|
||||||
|
}
|
|
@ -0,0 +1,118 @@
|
||||||
|
package common
|
||||||
|
|
||||||
|
import "database/sql"
|
||||||
|
import "../query_gen/lib"
|
||||||
|
|
||||||
|
var Polls PollStore
|
||||||
|
|
||||||
|
type Poll struct {
|
||||||
|
ID int
|
||||||
|
Type int // 0: Single choice, 1: Multiple choice, 2: Multiple choice w/ points
|
||||||
|
//AntiCheat bool // Apply various mitigations for cheating
|
||||||
|
// GroupPower map[gid]points // The number of points a group can spend in this poll, defaults to 1
|
||||||
|
|
||||||
|
Options []string
|
||||||
|
Results map[int]int // map[optionIndex]points
|
||||||
|
VoteCount int
|
||||||
|
}
|
||||||
|
|
||||||
|
type Pollable interface {
|
||||||
|
SetPoll(pollID int) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type PollStore interface {
|
||||||
|
Get(id int) (*Poll, error)
|
||||||
|
Exists(id int) bool
|
||||||
|
Create(parent Pollable, pollType int, pollOptions []string) (int, error)
|
||||||
|
Reload(id int) error
|
||||||
|
//GlobalCount() int
|
||||||
|
|
||||||
|
SetCache(cache PollCache)
|
||||||
|
GetCache() PollCache
|
||||||
|
}
|
||||||
|
|
||||||
|
type DefaultPollStore struct {
|
||||||
|
cache PollCache
|
||||||
|
|
||||||
|
get *sql.Stmt
|
||||||
|
exists *sql.Stmt
|
||||||
|
create *sql.Stmt
|
||||||
|
delete *sql.Stmt
|
||||||
|
//pollCount *sql.Stmt
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDefaultPollStore(cache PollCache) (*DefaultPollStore, error) {
|
||||||
|
acc := qgen.Builder.Accumulator()
|
||||||
|
if cache == nil {
|
||||||
|
cache = NewNullPollCache()
|
||||||
|
}
|
||||||
|
// TODO: Add an admin version of registerStmt with more flexibility?
|
||||||
|
return &DefaultPollStore{
|
||||||
|
cache: cache,
|
||||||
|
get: acc.Select("polls").Columns("type, options, votes").Where("pollID = ?").Prepare(),
|
||||||
|
exists: acc.Select("polls").Columns("pollID").Where("pollID = ?").Prepare(),
|
||||||
|
create: acc.Insert("polls").Columns("type, options").Fields("?,?").Prepare(),
|
||||||
|
//pollCount: acc.SimpleCount("polls", "", ""),
|
||||||
|
}, acc.FirstError()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (store *DefaultPollStore) Exists(id int) bool {
|
||||||
|
err := store.exists.QueryRow(id).Scan(&id)
|
||||||
|
if err != nil && err != ErrNoRows {
|
||||||
|
LogError(err)
|
||||||
|
}
|
||||||
|
return err != ErrNoRows
|
||||||
|
}
|
||||||
|
|
||||||
|
func (store *DefaultPollStore) Get(id int) (*Poll, error) {
|
||||||
|
poll, err := store.cache.Get(id)
|
||||||
|
if err == nil {
|
||||||
|
return poll, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
poll = &Poll{ID: id}
|
||||||
|
var optionTxt []byte
|
||||||
|
err = store.get.QueryRow(id).Scan(&poll.Type, optionTxt, &poll.VoteCount)
|
||||||
|
if err == nil {
|
||||||
|
store.cache.Set(poll)
|
||||||
|
}
|
||||||
|
return poll, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (store *DefaultPollStore) Reload(id int) error {
|
||||||
|
poll := &Poll{ID: id}
|
||||||
|
var optionTxt []byte
|
||||||
|
err := store.get.QueryRow(id).Scan(&poll.Type, optionTxt, &poll.VoteCount)
|
||||||
|
if err != nil {
|
||||||
|
store.cache.Remove(id)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_ = store.cache.Set(poll)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (store *DefaultPollStore) Create(parent Pollable, pollType int, pollOptions []string) (id int, err error) {
|
||||||
|
res, err := store.create.Exec(pollType, pollOptions)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
lastID, err := res.LastInsertId()
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return int(lastID), parent.SetPoll(int(lastID)) // TODO: Delete the poll if SetPoll fails
|
||||||
|
}
|
||||||
|
|
||||||
|
func (store *DefaultPollStore) SetCache(cache PollCache) {
|
||||||
|
store.cache = cache
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: We're temporarily doing this so that you can do ucache != nil in getTopicUser. Refactor it.
|
||||||
|
func (store *DefaultPollStore) GetCache() PollCache {
|
||||||
|
_, ok := store.cache.(*NullPollCache)
|
||||||
|
if ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return store.cache
|
||||||
|
}
|
|
@ -66,6 +66,7 @@ type ReplyStmts struct {
|
||||||
isLiked *sql.Stmt
|
isLiked *sql.Stmt
|
||||||
createLike *sql.Stmt
|
createLike *sql.Stmt
|
||||||
edit *sql.Stmt
|
edit *sql.Stmt
|
||||||
|
setPoll *sql.Stmt
|
||||||
delete *sql.Stmt
|
delete *sql.Stmt
|
||||||
addLikesToReply *sql.Stmt
|
addLikesToReply *sql.Stmt
|
||||||
removeRepliesFromTopic *sql.Stmt
|
removeRepliesFromTopic *sql.Stmt
|
||||||
|
@ -76,7 +77,8 @@ func init() {
|
||||||
replyStmts = ReplyStmts{
|
replyStmts = ReplyStmts{
|
||||||
isLiked: acc.Select("likes").Columns("targetItem").Where("sentBy = ? and targetItem = ? and targetType = 'replies'").Prepare(),
|
isLiked: acc.Select("likes").Columns("targetItem").Where("sentBy = ? and targetItem = ? and targetType = 'replies'").Prepare(),
|
||||||
createLike: acc.Insert("likes").Columns("weight, targetItem, targetType, sentBy").Fields("?,?,?,?").Prepare(),
|
createLike: acc.Insert("likes").Columns("weight, targetItem, targetType, sentBy").Fields("?,?,?,?").Prepare(),
|
||||||
edit: acc.Update("replies").Set("content = ?, parsed_content = ?, poll = ?").Where("rid = ?").Prepare(),
|
edit: acc.Update("replies").Set("content = ?, parsed_content = ?").Where("rid = ? AND poll = 0").Prepare(),
|
||||||
|
setPoll: acc.Update("replies").Set("content = '', parsed_content = '', poll = ?").Where("rid = ? AND poll = 0").Prepare(),
|
||||||
delete: acc.Delete("replies").Where("rid = ?").Prepare(),
|
delete: acc.Delete("replies").Where("rid = ?").Prepare(),
|
||||||
addLikesToReply: acc.Update("replies").Set("likeCount = likeCount + ?").Where("rid = ?").Prepare(),
|
addLikesToReply: acc.Update("replies").Set("likeCount = likeCount + ?").Where("rid = ?").Prepare(),
|
||||||
removeRepliesFromTopic: acc.Update("topics").Set("postCount = postCount - ?").Where("tid = ?").Prepare(),
|
removeRepliesFromTopic: acc.Update("topics").Set("postCount = postCount - ?").Where("tid = ?").Prepare(),
|
||||||
|
@ -127,12 +129,13 @@ func (reply *Reply) SetPost(content string) error {
|
||||||
}
|
}
|
||||||
content = PreparseMessage(html.UnescapeString(content))
|
content = PreparseMessage(html.UnescapeString(content))
|
||||||
parsedContent := ParseMessage(content, topic.ParentID, "forums")
|
parsedContent := ParseMessage(content, topic.ParentID, "forums")
|
||||||
_, err = replyStmts.edit.Exec(content, parsedContent, 0, reply.ID)
|
_, err = replyStmts.edit.Exec(content, parsedContent, reply.ID) // TODO: Sniff if this changed anything to see if we hit an existing poll
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (reply *Reply) SetPoll(content string) error {
|
func (reply *Reply) SetPoll(pollID int) error {
|
||||||
return nil
|
_, err := replyStmts.setPoll.Exec(pollID, reply.ID) // TODO: Sniff if this changed anything to see if we hit a poll
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (reply *Reply) Topic() (*Topic, error) {
|
func (reply *Reply) Topic() (*Topic, error) {
|
||||||
|
|
|
@ -22,6 +22,7 @@ type site struct {
|
||||||
Name string
|
Name string
|
||||||
Email string
|
Email string
|
||||||
URL string
|
URL string
|
||||||
|
Host string
|
||||||
Port string
|
Port string
|
||||||
EnableSsl bool
|
EnableSsl bool
|
||||||
EnableEmails bool
|
EnableEmails bool
|
||||||
|
@ -82,6 +83,7 @@ type devConfig struct {
|
||||||
|
|
||||||
func ProcessConfig() error {
|
func ProcessConfig() error {
|
||||||
Config.Noavatar = strings.Replace(Config.Noavatar, "{site_url}", Site.URL, -1)
|
Config.Noavatar = strings.Replace(Config.Noavatar, "{site_url}", Site.URL, -1)
|
||||||
|
Site.Host = Site.URL
|
||||||
if Site.Port != "80" && Site.Port != "443" {
|
if Site.Port != "80" && Site.Port != "443" {
|
||||||
Site.URL = strings.TrimSuffix(Site.URL, "/")
|
Site.URL = strings.TrimSuffix(Site.URL, "/")
|
||||||
Site.URL = strings.TrimSuffix(Site.URL, "\\")
|
Site.URL = strings.TrimSuffix(Site.URL, "\\")
|
||||||
|
|
|
@ -120,6 +120,7 @@ type TopicStmts struct {
|
||||||
addLikesToTopic *sql.Stmt
|
addLikesToTopic *sql.Stmt
|
||||||
delete *sql.Stmt
|
delete *sql.Stmt
|
||||||
edit *sql.Stmt
|
edit *sql.Stmt
|
||||||
|
setPoll *sql.Stmt
|
||||||
createActionReply *sql.Stmt
|
createActionReply *sql.Stmt
|
||||||
|
|
||||||
getTopicUser *sql.Stmt // TODO: Can we get rid of this?
|
getTopicUser *sql.Stmt // TODO: Can we get rid of this?
|
||||||
|
@ -141,7 +142,8 @@ func init() {
|
||||||
createLike: acc.Insert("likes").Columns("weight, targetItem, targetType, sentBy").Fields("?,?,?,?").Prepare(),
|
createLike: acc.Insert("likes").Columns("weight, targetItem, targetType, sentBy").Fields("?,?,?,?").Prepare(),
|
||||||
addLikesToTopic: acc.Update("topics").Set("likeCount = likeCount + ?").Where("tid = ?").Prepare(),
|
addLikesToTopic: acc.Update("topics").Set("likeCount = likeCount + ?").Where("tid = ?").Prepare(),
|
||||||
delete: acc.Delete("topics").Where("tid = ?").Prepare(),
|
delete: acc.Delete("topics").Where("tid = ?").Prepare(),
|
||||||
edit: acc.Update("topics").Set("title = ?, content = ?, parsed_content = ?").Where("tid = ?").Prepare(),
|
edit: acc.Update("topics").Set("title = ?, content = ?, parsed_content = ?").Where("tid = ?").Prepare(), // TODO: Only run the content update bits on non-polls, does this matter?
|
||||||
|
setPoll: acc.Update("topics").Set("content = '', parsed_content = '', poll = ?").Where("tid = ? AND poll = 0").Prepare(),
|
||||||
createActionReply: acc.Insert("replies").Columns("tid, actionType, ipaddress, createdBy, createdAt, lastUpdated, content, parsed_content").Fields("?,?,?,?,UTC_TIMESTAMP(),UTC_TIMESTAMP(),'',''").Prepare(),
|
createActionReply: acc.Insert("replies").Columns("tid, actionType, ipaddress, createdBy, createdAt, lastUpdated, content, parsed_content").Fields("?,?,?,?,UTC_TIMESTAMP(),UTC_TIMESTAMP(),'',''").Prepare(),
|
||||||
|
|
||||||
getTopicUser: acc.SimpleLeftJoin("topics", "users", "topics.title, topics.content, topics.createdBy, topics.createdAt, topics.is_closed, topics.sticky, topics.parentID, topics.ipaddress, topics.postCount, topics.likeCount, users.name, users.avatar, users.group, users.url_prefix, users.url_name, users.level", "topics.createdBy = users.uid", "tid = ?", "", ""),
|
getTopicUser: acc.SimpleLeftJoin("topics", "users", "topics.title, topics.content, topics.createdBy, topics.createdAt, topics.is_closed, topics.sticky, topics.parentID, topics.ipaddress, topics.postCount, topics.likeCount, users.name, users.avatar, users.group, users.url_prefix, users.url_name, users.level", "topics.createdBy = users.uid", "tid = ?", "", ""),
|
||||||
|
@ -257,6 +259,11 @@ func (topic *Topic) Update(name string, content string) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (topic *Topic) SetPoll(pollID int) error {
|
||||||
|
_, err := topicStmts.setPoll.Exec(pollID, topic.ID) // TODO: Sniff if this changed anything to see if we hit an existing poll
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: Have this go through the ReplyStore?
|
// TODO: Have this go through the ReplyStore?
|
||||||
func (topic *Topic) CreateActionReply(action string, ipaddress string, user User) (err error) {
|
func (topic *Topic) CreateActionReply(action string, ipaddress string, user User) (err error) {
|
||||||
_, err = topicStmts.createActionReply.Exec(topic.ID, action, ipaddress, user.ID)
|
_, err = topicStmts.createActionReply.Exec(topic.ID, action, ipaddress, user.ID)
|
||||||
|
|
|
@ -311,8 +311,10 @@ var agentMapEnum = map[string]int{
|
||||||
"duckduckgo": 11,
|
"duckduckgo": 11,
|
||||||
"discord": 12,
|
"discord": 12,
|
||||||
"cloudflarealwayson": 13,
|
"cloudflarealwayson": 13,
|
||||||
"lynx": 14,
|
"uptimebot": 14,
|
||||||
"blank": 15,
|
"lynx": 15,
|
||||||
|
"blank": 16,
|
||||||
|
"malformed": 17,
|
||||||
}
|
}
|
||||||
var reverseAgentMapEnum = map[int]string{
|
var reverseAgentMapEnum = map[int]string{
|
||||||
0: "unknown",
|
0: "unknown",
|
||||||
|
@ -329,8 +331,10 @@ var reverseAgentMapEnum = map[int]string{
|
||||||
11: "duckduckgo",
|
11: "duckduckgo",
|
||||||
12: "discord",
|
12: "discord",
|
||||||
13: "cloudflarealwayson",
|
13: "cloudflarealwayson",
|
||||||
14: "lynx",
|
14: "uptimebot",
|
||||||
15: "blank",
|
15: "lynx",
|
||||||
|
16: "blank",
|
||||||
|
17: "malformed",
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Stop spilling these into the package scope?
|
// TODO: Stop spilling these into the package scope?
|
||||||
|
@ -392,20 +396,23 @@ func (router *GenRouter) RemoveFunc(pattern string) error {
|
||||||
// TODO: GetDefaultRoute
|
// TODO: GetDefaultRoute
|
||||||
|
|
||||||
func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||||
if len(req.URL.Path) == 0 || req.URL.Path[0] != '/' {
|
if len(req.URL.Path) == 0 || req.URL.Path[0] != '/' || req.Host != common.Site.Host {
|
||||||
w.WriteHeader(405)
|
w.WriteHeader(405)
|
||||||
w.Write([]byte(""))
|
w.Write([]byte(""))
|
||||||
log.Print("Suspicious UA: ", req.UserAgent())
|
log.Print("Malformed Request")
|
||||||
|
log.Print("UA: ", req.UserAgent())
|
||||||
log.Print("Method: ", req.Method)
|
log.Print("Method: ", req.Method)
|
||||||
for key, value := range req.Header {
|
for key, value := range req.Header {
|
||||||
for _, vvalue := range value {
|
for _, vvalue := range value {
|
||||||
log.Print("Header '" + key + "': " + vvalue + "!!")
|
log.Print("Header '" + key + "': " + vvalue + "!!")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
log.Print("req.Host: ", req.Host)
|
||||||
log.Print("req.URL.Path: ", req.URL.Path)
|
log.Print("req.URL.Path: ", req.URL.Path)
|
||||||
log.Print("req.URL.RawQuery: ", req.URL.RawQuery)
|
log.Print("req.URL.RawQuery: ", req.URL.RawQuery)
|
||||||
log.Print("req.Referer(): ", req.Referer())
|
log.Print("req.Referer(): ", req.Referer())
|
||||||
log.Print("req.RemoteAddr: ", req.RemoteAddr)
|
log.Print("req.RemoteAddr: ", req.RemoteAddr)
|
||||||
|
common.AgentViewCounter.Bump(17)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -420,6 +427,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||||
log.Print("Header '" + key + "': " + vvalue + "!!")
|
log.Print("Header '" + key + "': " + vvalue + "!!")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
log.Print("req.Host: ", req.Host)
|
||||||
log.Print("req.URL.Path: ", req.URL.Path)
|
log.Print("req.URL.Path: ", req.URL.Path)
|
||||||
log.Print("req.URL.RawQuery: ", req.URL.RawQuery)
|
log.Print("req.URL.RawQuery: ", req.URL.RawQuery)
|
||||||
log.Print("req.Referer(): ", req.Referer())
|
log.Print("req.Referer(): ", req.Referer())
|
||||||
|
@ -435,6 +443,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||||
log.Print("Header '" + key + "': " + vvalue + "!!")
|
log.Print("Header '" + key + "': " + vvalue + "!!")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
log.Print("req.Host: ", req.Host)
|
||||||
log.Print("req.URL.Path: ", req.URL.Path)
|
log.Print("req.URL.Path: ", req.URL.Path)
|
||||||
log.Print("req.URL.RawQuery: ", req.URL.RawQuery)
|
log.Print("req.URL.RawQuery: ", req.URL.RawQuery)
|
||||||
log.Print("req.Referer(): ", req.Referer())
|
log.Print("req.Referer(): ", req.Referer())
|
||||||
|
@ -458,6 +467,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
log.Print("prefix: ", prefix)
|
log.Print("prefix: ", prefix)
|
||||||
|
log.Print("req.Host: ", req.Host)
|
||||||
log.Print("req.URL.Path: ", req.URL.Path)
|
log.Print("req.URL.Path: ", req.URL.Path)
|
||||||
log.Print("req.URL.RawQuery: ", req.URL.RawQuery)
|
log.Print("req.URL.RawQuery: ", req.URL.RawQuery)
|
||||||
log.Print("extraData: ", extraData)
|
log.Print("extraData: ", extraData)
|
||||||
|
@ -507,11 +517,13 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||||
case strings.Contains(ua,"Discordbot"):
|
case strings.Contains(ua,"Discordbot"):
|
||||||
common.AgentViewCounter.Bump(12)
|
common.AgentViewCounter.Bump(12)
|
||||||
case strings.Contains(ua,"Lynx"):
|
case strings.Contains(ua,"Lynx"):
|
||||||
common.AgentViewCounter.Bump(14)
|
common.AgentViewCounter.Bump(15)
|
||||||
case strings.Contains(ua,"CloudFlare-AlwaysOnline"):
|
case strings.Contains(ua,"CloudFlare-AlwaysOnline"):
|
||||||
common.AgentViewCounter.Bump(13)
|
common.AgentViewCounter.Bump(13)
|
||||||
|
case strings.Contains(ua,"Uptimebot"):
|
||||||
|
common.AgentViewCounter.Bump(14)
|
||||||
case ua == "":
|
case ua == "":
|
||||||
common.AgentViewCounter.Bump(15)
|
common.AgentViewCounter.Bump(16)
|
||||||
if common.Dev.DebugMode {
|
if common.Dev.DebugMode {
|
||||||
log.Print("Blank UA: ", req.UserAgent())
|
log.Print("Blank UA: ", req.UserAgent())
|
||||||
log.Print("Method: ", req.Method)
|
log.Print("Method: ", req.Method)
|
||||||
|
@ -522,6 +534,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
log.Print("prefix: ", prefix)
|
log.Print("prefix: ", prefix)
|
||||||
|
log.Print("req.Host: ", req.Host)
|
||||||
log.Print("req.URL.Path: ", req.URL.Path)
|
log.Print("req.URL.Path: ", req.URL.Path)
|
||||||
log.Print("req.URL.RawQuery: ", req.URL.RawQuery)
|
log.Print("req.URL.RawQuery: ", req.URL.RawQuery)
|
||||||
log.Print("extraData: ", extraData)
|
log.Print("extraData: ", extraData)
|
||||||
|
@ -539,6 +552,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
log.Print("prefix: ", prefix)
|
log.Print("prefix: ", prefix)
|
||||||
|
log.Print("req.Host: ", req.Host)
|
||||||
log.Print("req.URL.Path: ", req.URL.Path)
|
log.Print("req.URL.Path: ", req.URL.Path)
|
||||||
log.Print("req.URL.RawQuery: ", req.URL.RawQuery)
|
log.Print("req.URL.RawQuery: ", req.URL.RawQuery)
|
||||||
log.Print("extraData: ", extraData)
|
log.Print("extraData: ", extraData)
|
||||||
|
|
|
@ -2,14 +2,15 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
var dbTablePrimaryKeys = map[string]string{
|
var dbTablePrimaryKeys = map[string]string{
|
||||||
"replies":"rid",
|
|
||||||
"attachments":"attachID",
|
|
||||||
"users_replies":"rid",
|
|
||||||
"activity_stream":"asid",
|
|
||||||
"word_filters":"wfid",
|
|
||||||
"forums":"fid",
|
|
||||||
"users_groups":"gid",
|
"users_groups":"gid",
|
||||||
"users_groups_scheduler":"uid",
|
"users_groups_scheduler":"uid",
|
||||||
"topics":"tid",
|
"replies":"rid",
|
||||||
|
"word_filters":"wfid",
|
||||||
|
"users_replies":"rid",
|
||||||
|
"activity_stream":"asid",
|
||||||
"users":"uid",
|
"users":"uid",
|
||||||
|
"forums":"fid",
|
||||||
|
"topics":"tid",
|
||||||
|
"attachments":"attachID",
|
||||||
|
"polls":"pollID",
|
||||||
}
|
}
|
||||||
|
|
4
main.go
4
main.go
|
@ -92,6 +92,10 @@ func afterDBInit() (err error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
common.Polls, err = common.NewDefaultPollStore(common.NewMemoryPollCache(100)) // TODO: Max number of polls held in cache, make this a config item
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
common.GlobalViewCounter, err = common.NewGlobalViewCounter()
|
common.GlobalViewCounter, err = common.NewGlobalViewCounter()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -591,5 +591,8 @@ $(document).ready(function(){
|
||||||
if(event.which == 39) this.querySelectorAll("#nextFloat a")[0].click();
|
if(event.which == 39) this.querySelectorAll("#nextFloat a")[0].click();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
$("#add_poll_button").click(function(event){
|
||||||
|
event.preventDefault();
|
||||||
|
$(".poll_content_row").removeClass("auto_hide");
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -60,6 +60,9 @@ func (adapter *MssqlAdapter) CreateTable(name string, table string, charset stri
|
||||||
case "text":
|
case "text":
|
||||||
column.Type = "nvarchar"
|
column.Type = "nvarchar"
|
||||||
max = true
|
max = true
|
||||||
|
case "json":
|
||||||
|
column.Type = "nvarchar"
|
||||||
|
max = true
|
||||||
case "boolean":
|
case "boolean":
|
||||||
column.Type = "bit"
|
column.Type = "bit"
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,6 +49,8 @@ func (adapter *MysqlAdapter) CreateTable(name string, table string, charset stri
|
||||||
// Make it easier to support Cassandra in the future
|
// Make it easier to support Cassandra in the future
|
||||||
if column.Type == "createdAt" {
|
if column.Type == "createdAt" {
|
||||||
column.Type = "datetime"
|
column.Type = "datetime"
|
||||||
|
} else if column.Type == "json" {
|
||||||
|
column.Type = "text"
|
||||||
}
|
}
|
||||||
|
|
||||||
var size string
|
var size string
|
||||||
|
|
|
@ -162,6 +162,7 @@ func createTables(adapter qgen.Adapter) error {
|
||||||
qgen.DBTableColumn{"words", "int", 0, false, false, "0"},
|
qgen.DBTableColumn{"words", "int", 0, false, false, "0"},
|
||||||
qgen.DBTableColumn{"views", "int", 0, false, false, "0"},
|
qgen.DBTableColumn{"views", "int", 0, false, false, "0"},
|
||||||
qgen.DBTableColumn{"css_class", "varchar", 100, false, false, "''"},
|
qgen.DBTableColumn{"css_class", "varchar", 100, false, false, "''"},
|
||||||
|
qgen.DBTableColumn{"poll", "int", 0, false, false, "0"},
|
||||||
qgen.DBTableColumn{"data", "varchar", 200, false, false, "''"},
|
qgen.DBTableColumn{"data", "varchar", 200, false, false, "''"},
|
||||||
},
|
},
|
||||||
[]qgen.DBTableKey{
|
[]qgen.DBTableKey{
|
||||||
|
@ -184,7 +185,7 @@ func createTables(adapter qgen.Adapter) error {
|
||||||
qgen.DBTableColumn{"likeCount", "int", 0, false, false, "0"},
|
qgen.DBTableColumn{"likeCount", "int", 0, false, false, "0"},
|
||||||
qgen.DBTableColumn{"words", "int", 0, false, false, "1"}, // ? - replies has a default of 1 and topics has 0? why?
|
qgen.DBTableColumn{"words", "int", 0, false, false, "1"}, // ? - replies has a default of 1 and topics has 0? why?
|
||||||
qgen.DBTableColumn{"actionType", "varchar", 20, false, false, "''"},
|
qgen.DBTableColumn{"actionType", "varchar", 20, false, false, "''"},
|
||||||
qgen.DBTableColumn{"poll", "boolean", 0, false, false, "0"},
|
qgen.DBTableColumn{"poll", "int", 0, false, false, "0"},
|
||||||
},
|
},
|
||||||
[]qgen.DBTableKey{
|
[]qgen.DBTableKey{
|
||||||
qgen.DBTableKey{"rid", "primary"},
|
qgen.DBTableKey{"rid", "primary"},
|
||||||
|
@ -217,6 +218,27 @@ func createTables(adapter qgen.Adapter) error {
|
||||||
[]qgen.DBTableKey{},
|
[]qgen.DBTableKey{},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
qgen.Install.CreateTable("polls", "utf8mb4", "utf8mb4_general_ci",
|
||||||
|
[]qgen.DBTableColumn{
|
||||||
|
qgen.DBTableColumn{"pollID", "int", 0, false, true, ""},
|
||||||
|
qgen.DBTableColumn{"type", "int", 0, false, false, "0"},
|
||||||
|
qgen.DBTableColumn{"options", "json", 0, false, false, ""},
|
||||||
|
qgen.DBTableColumn{"votes", "int", 0, false, false, "0"},
|
||||||
|
},
|
||||||
|
[]qgen.DBTableKey{
|
||||||
|
qgen.DBTableKey{"pollID", "primary"},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
qgen.Install.CreateTable("polls_voters", "utf8mb4", "utf8mb4_general_ci",
|
||||||
|
[]qgen.DBTableColumn{
|
||||||
|
qgen.DBTableColumn{"pollID", "int", 0, false, false, ""},
|
||||||
|
qgen.DBTableColumn{"uid", "int", 0, false, false, ""},
|
||||||
|
qgen.DBTableColumn{"option", "int", 0, false, false, "0"},
|
||||||
|
},
|
||||||
|
[]qgen.DBTableKey{},
|
||||||
|
)
|
||||||
|
|
||||||
qgen.Install.CreateTable("users_replies", "utf8mb4", "utf8mb4_general_ci",
|
qgen.Install.CreateTable("users_replies", "utf8mb4", "utf8mb4_general_ci",
|
||||||
[]qgen.DBTableColumn{
|
[]qgen.DBTableColumn{
|
||||||
qgen.DBTableColumn{"rid", "int", 0, false, true, ""},
|
qgen.DBTableColumn{"rid", "int", 0, false, true, ""},
|
||||||
|
|
|
@ -173,8 +173,10 @@ func main() {
|
||||||
"duckduckgo",
|
"duckduckgo",
|
||||||
"discord",
|
"discord",
|
||||||
"cloudflarealwayson",
|
"cloudflarealwayson",
|
||||||
|
"uptimebot",
|
||||||
"lynx",
|
"lynx",
|
||||||
"blank",
|
"blank",
|
||||||
|
"malformed",
|
||||||
}
|
}
|
||||||
|
|
||||||
tmplVars.AllAgentMap = make(map[string]int)
|
tmplVars.AllAgentMap = make(map[string]int)
|
||||||
|
@ -276,20 +278,23 @@ func (router *GenRouter) RemoveFunc(pattern string) error {
|
||||||
// TODO: GetDefaultRoute
|
// TODO: GetDefaultRoute
|
||||||
|
|
||||||
func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||||
if len(req.URL.Path) == 0 || req.URL.Path[0] != '/' {
|
if len(req.URL.Path) == 0 || req.URL.Path[0] != '/' || req.Host != common.Site.Host {
|
||||||
w.WriteHeader(405)
|
w.WriteHeader(405)
|
||||||
w.Write([]byte(""))
|
w.Write([]byte(""))
|
||||||
log.Print("Suspicious UA: ", req.UserAgent())
|
log.Print("Malformed Request")
|
||||||
|
log.Print("UA: ", req.UserAgent())
|
||||||
log.Print("Method: ", req.Method)
|
log.Print("Method: ", req.Method)
|
||||||
for key, value := range req.Header {
|
for key, value := range req.Header {
|
||||||
for _, vvalue := range value {
|
for _, vvalue := range value {
|
||||||
log.Print("Header '" + key + "': " + vvalue + "!!")
|
log.Print("Header '" + key + "': " + vvalue + "!!")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
log.Print("req.Host: ", req.Host)
|
||||||
log.Print("req.URL.Path: ", req.URL.Path)
|
log.Print("req.URL.Path: ", req.URL.Path)
|
||||||
log.Print("req.URL.RawQuery: ", req.URL.RawQuery)
|
log.Print("req.URL.RawQuery: ", req.URL.RawQuery)
|
||||||
log.Print("req.Referer(): ", req.Referer())
|
log.Print("req.Referer(): ", req.Referer())
|
||||||
log.Print("req.RemoteAddr: ", req.RemoteAddr)
|
log.Print("req.RemoteAddr: ", req.RemoteAddr)
|
||||||
|
common.AgentViewCounter.Bump({{.AllAgentMap.malformed}})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -304,6 +309,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||||
log.Print("Header '" + key + "': " + vvalue + "!!")
|
log.Print("Header '" + key + "': " + vvalue + "!!")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
log.Print("req.Host: ", req.Host)
|
||||||
log.Print("req.URL.Path: ", req.URL.Path)
|
log.Print("req.URL.Path: ", req.URL.Path)
|
||||||
log.Print("req.URL.RawQuery: ", req.URL.RawQuery)
|
log.Print("req.URL.RawQuery: ", req.URL.RawQuery)
|
||||||
log.Print("req.Referer(): ", req.Referer())
|
log.Print("req.Referer(): ", req.Referer())
|
||||||
|
@ -319,6 +325,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||||
log.Print("Header '" + key + "': " + vvalue + "!!")
|
log.Print("Header '" + key + "': " + vvalue + "!!")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
log.Print("req.Host: ", req.Host)
|
||||||
log.Print("req.URL.Path: ", req.URL.Path)
|
log.Print("req.URL.Path: ", req.URL.Path)
|
||||||
log.Print("req.URL.RawQuery: ", req.URL.RawQuery)
|
log.Print("req.URL.RawQuery: ", req.URL.RawQuery)
|
||||||
log.Print("req.Referer(): ", req.Referer())
|
log.Print("req.Referer(): ", req.Referer())
|
||||||
|
@ -342,6 +349,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
log.Print("prefix: ", prefix)
|
log.Print("prefix: ", prefix)
|
||||||
|
log.Print("req.Host: ", req.Host)
|
||||||
log.Print("req.URL.Path: ", req.URL.Path)
|
log.Print("req.URL.Path: ", req.URL.Path)
|
||||||
log.Print("req.URL.RawQuery: ", req.URL.RawQuery)
|
log.Print("req.URL.RawQuery: ", req.URL.RawQuery)
|
||||||
log.Print("extraData: ", extraData)
|
log.Print("extraData: ", extraData)
|
||||||
|
@ -394,6 +402,8 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||||
common.AgentViewCounter.Bump({{.AllAgentMap.lynx}})
|
common.AgentViewCounter.Bump({{.AllAgentMap.lynx}})
|
||||||
case strings.Contains(ua,"CloudFlare-AlwaysOnline"):
|
case strings.Contains(ua,"CloudFlare-AlwaysOnline"):
|
||||||
common.AgentViewCounter.Bump({{.AllAgentMap.cloudflarealwayson}})
|
common.AgentViewCounter.Bump({{.AllAgentMap.cloudflarealwayson}})
|
||||||
|
case strings.Contains(ua,"Uptimebot"):
|
||||||
|
common.AgentViewCounter.Bump({{.AllAgentMap.uptimebot}})
|
||||||
case ua == "":
|
case ua == "":
|
||||||
common.AgentViewCounter.Bump({{.AllAgentMap.blank}})
|
common.AgentViewCounter.Bump({{.AllAgentMap.blank}})
|
||||||
if common.Dev.DebugMode {
|
if common.Dev.DebugMode {
|
||||||
|
@ -406,6 +416,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
log.Print("prefix: ", prefix)
|
log.Print("prefix: ", prefix)
|
||||||
|
log.Print("req.Host: ", req.Host)
|
||||||
log.Print("req.URL.Path: ", req.URL.Path)
|
log.Print("req.URL.Path: ", req.URL.Path)
|
||||||
log.Print("req.URL.RawQuery: ", req.URL.RawQuery)
|
log.Print("req.URL.RawQuery: ", req.URL.RawQuery)
|
||||||
log.Print("extraData: ", extraData)
|
log.Print("extraData: ", extraData)
|
||||||
|
@ -423,6 +434,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
log.Print("prefix: ", prefix)
|
log.Print("prefix: ", prefix)
|
||||||
|
log.Print("req.Host: ", req.Host)
|
||||||
log.Print("req.URL.Path: ", req.URL.Path)
|
log.Print("req.URL.Path: ", req.URL.Path)
|
||||||
log.Print("req.URL.RawQuery: ", req.URL.RawQuery)
|
log.Print("req.URL.RawQuery: ", req.URL.RawQuery)
|
||||||
log.Print("extraData: ", extraData)
|
log.Print("extraData: ", extraData)
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
CREATE TABLE [polls] (
|
||||||
|
[pollID] int not null IDENTITY,
|
||||||
|
[type] int DEFAULT 0 not null,
|
||||||
|
[options] nvarchar (MAX) not null,
|
||||||
|
[votes] int DEFAULT 0 not null,
|
||||||
|
primary key([pollID])
|
||||||
|
);
|
|
@ -0,0 +1,5 @@
|
||||||
|
CREATE TABLE [polls_voters] (
|
||||||
|
[pollID] int not null,
|
||||||
|
[uid] int not null,
|
||||||
|
[option] int DEFAULT 0 not null
|
||||||
|
);
|
|
@ -12,6 +12,6 @@ CREATE TABLE [replies] (
|
||||||
[likeCount] int DEFAULT 0 not null,
|
[likeCount] int DEFAULT 0 not null,
|
||||||
[words] int DEFAULT 1 not null,
|
[words] int DEFAULT 1 not null,
|
||||||
[actionType] nvarchar (20) DEFAULT '' not null,
|
[actionType] nvarchar (20) DEFAULT '' not null,
|
||||||
[poll] bit DEFAULT 0 not null,
|
[poll] int DEFAULT 0 not null,
|
||||||
primary key([rid])
|
primary key([rid])
|
||||||
);
|
);
|
|
@ -16,6 +16,7 @@ CREATE TABLE [topics] (
|
||||||
[words] int DEFAULT 0 not null,
|
[words] int DEFAULT 0 not null,
|
||||||
[views] int DEFAULT 0 not null,
|
[views] int DEFAULT 0 not null,
|
||||||
[css_class] nvarchar (100) DEFAULT '' not null,
|
[css_class] nvarchar (100) DEFAULT '' not null,
|
||||||
|
[poll] int DEFAULT 0 not null,
|
||||||
[data] nvarchar (200) DEFAULT '' not null,
|
[data] nvarchar (200) DEFAULT '' not null,
|
||||||
primary key([tid])
|
primary key([tid])
|
||||||
);
|
);
|
|
@ -0,0 +1,7 @@
|
||||||
|
CREATE TABLE `polls` (
|
||||||
|
`pollID` int not null AUTO_INCREMENT,
|
||||||
|
`type` int DEFAULT 0 not null,
|
||||||
|
`options` text not null,
|
||||||
|
`votes` int DEFAULT 0 not null,
|
||||||
|
primary key(`pollID`)
|
||||||
|
) CHARSET=utf8mb4 COLLATE utf8mb4_general_ci;
|
|
@ -0,0 +1,5 @@
|
||||||
|
CREATE TABLE `polls_voters` (
|
||||||
|
`pollID` int not null,
|
||||||
|
`uid` int not null,
|
||||||
|
`option` int DEFAULT 0 not null
|
||||||
|
) CHARSET=utf8mb4 COLLATE utf8mb4_general_ci;
|
|
@ -12,6 +12,6 @@ CREATE TABLE `replies` (
|
||||||
`likeCount` int DEFAULT 0 not null,
|
`likeCount` int DEFAULT 0 not null,
|
||||||
`words` int DEFAULT 1 not null,
|
`words` int DEFAULT 1 not null,
|
||||||
`actionType` varchar(20) DEFAULT '' not null,
|
`actionType` varchar(20) DEFAULT '' not null,
|
||||||
`poll` boolean DEFAULT 0 not null,
|
`poll` int DEFAULT 0 not null,
|
||||||
primary key(`rid`)
|
primary key(`rid`)
|
||||||
) CHARSET=utf8mb4 COLLATE utf8mb4_general_ci;
|
) CHARSET=utf8mb4 COLLATE utf8mb4_general_ci;
|
|
@ -16,6 +16,7 @@ CREATE TABLE `topics` (
|
||||||
`words` int DEFAULT 0 not null,
|
`words` int DEFAULT 0 not null,
|
||||||
`views` int DEFAULT 0 not null,
|
`views` int DEFAULT 0 not null,
|
||||||
`css_class` varchar(100) DEFAULT '' not null,
|
`css_class` varchar(100) DEFAULT '' not null,
|
||||||
|
`poll` int DEFAULT 0 not null,
|
||||||
`data` varchar(200) DEFAULT '' not null,
|
`data` varchar(200) DEFAULT '' not null,
|
||||||
primary key(`tid`)
|
primary key(`tid`)
|
||||||
) CHARSET=utf8mb4 COLLATE utf8mb4_general_ci;
|
) CHARSET=utf8mb4 COLLATE utf8mb4_general_ci;
|
|
@ -0,0 +1,7 @@
|
||||||
|
CREATE TABLE `polls` (
|
||||||
|
`pollID` serial not null,
|
||||||
|
`type` int DEFAULT 0 not null,
|
||||||
|
`options` json not null,
|
||||||
|
`votes` int DEFAULT 0 not null,
|
||||||
|
primary key(`pollID`)
|
||||||
|
);
|
|
@ -0,0 +1,5 @@
|
||||||
|
CREATE TABLE `polls_voters` (
|
||||||
|
`pollID` int not null,
|
||||||
|
`uid` int not null,
|
||||||
|
`option` int DEFAULT 0 not null
|
||||||
|
);
|
|
@ -12,6 +12,6 @@ CREATE TABLE `replies` (
|
||||||
`likeCount` int DEFAULT 0 not null,
|
`likeCount` int DEFAULT 0 not null,
|
||||||
`words` int DEFAULT 1 not null,
|
`words` int DEFAULT 1 not null,
|
||||||
`actionType` varchar (20) DEFAULT '' not null,
|
`actionType` varchar (20) DEFAULT '' not null,
|
||||||
`poll` boolean DEFAULT 0 not null,
|
`poll` int DEFAULT 0 not null,
|
||||||
primary key(`rid`)
|
primary key(`rid`)
|
||||||
);
|
);
|
|
@ -16,6 +16,7 @@ CREATE TABLE `topics` (
|
||||||
`words` int DEFAULT 0 not null,
|
`words` int DEFAULT 0 not null,
|
||||||
`views` int DEFAULT 0 not null,
|
`views` int DEFAULT 0 not null,
|
||||||
`css_class` varchar (100) DEFAULT '' not null,
|
`css_class` varchar (100) DEFAULT '' not null,
|
||||||
|
`poll` int DEFAULT 0 not null,
|
||||||
`data` varchar (200) DEFAULT '' not null,
|
`data` varchar (200) DEFAULT '' not null,
|
||||||
primary key(`tid`)
|
primary key(`tid`)
|
||||||
);
|
);
|
|
@ -900,9 +900,19 @@ var topics_23 = []byte(`
|
||||||
<textarea form="topic_create_form_form" id="input_content" name="topic-content" placeholder="Insert post here" required></textarea>
|
<textarea form="topic_create_form_form" id="input_content" name="topic-content" placeholder="Insert post here" required></textarea>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="formrow poll_content_row auto_hide">
|
||||||
|
<div class="formitem">
|
||||||
|
<div class="pollinput">
|
||||||
|
<input type="checkbox" disabled />
|
||||||
|
<label class="pollinputlabel"></label>
|
||||||
|
<input type="text" placeholder="Add new poll option" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="formrow quick_button_row">
|
<div class="formrow quick_button_row">
|
||||||
<div class="formitem">
|
<div class="formitem">
|
||||||
<button form="topic_create_form_form" class="formbutton">Create Topic</button>
|
<button form="topic_create_form_form" class="formbutton">Create Topic</button>
|
||||||
|
<button form="topic_create_form_form" class="formbutton" id="add_poll_button">Add Poll</button>
|
||||||
`)
|
`)
|
||||||
var topics_24 = []byte(`
|
var topics_24 = []byte(`
|
||||||
<input name="upload_files" form="topic_create_form_form" id="upload_files" multiple type="file" style="display: none;" />
|
<input name="upload_files" form="topic_create_form_form" id="upload_files" multiple type="file" style="display: none;" />
|
||||||
|
@ -1071,9 +1081,15 @@ var forum_21 = []byte(`" type="hidden">
|
||||||
<textarea form="topic_create_form_form" id="input_content" name="topic-content" placeholder="Insert post here" required></textarea>
|
<textarea form="topic_create_form_form" id="input_content" name="topic-content" placeholder="Insert post here" required></textarea>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="formrow poll_content_row auto_hide">
|
||||||
|
<div class="formitem">
|
||||||
|
Poll stuff
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="formrow quick_button_row">
|
<div class="formrow quick_button_row">
|
||||||
<div class="formitem">
|
<div class="formitem">
|
||||||
<button form="topic_create_form_form" name="topic-button" class="formbutton">Create Topic</button>
|
<button form="topic_create_form_form" name="topic-button" class="formbutton">Create Topic</button>
|
||||||
|
<button form="topic_create_form_form" class="formbutton" id="add_poll_button">Add Poll</button>
|
||||||
`)
|
`)
|
||||||
var forum_22 = []byte(`
|
var forum_22 = []byte(`
|
||||||
<input name="upload_files" form="topic_create_form_form" id="upload_files" multiple type="file" style="display: none;" />
|
<input name="upload_files" form="topic_create_form_form" id="upload_files" multiple type="file" style="display: none;" />
|
||||||
|
|
|
@ -55,9 +55,15 @@
|
||||||
<textarea form="topic_create_form_form" id="input_content" name="topic-content" placeholder="Insert post here" required></textarea>
|
<textarea form="topic_create_form_form" id="input_content" name="topic-content" placeholder="Insert post here" required></textarea>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="formrow poll_content_row auto_hide">
|
||||||
|
<div class="formitem">
|
||||||
|
Poll stuff
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="formrow quick_button_row">
|
<div class="formrow quick_button_row">
|
||||||
<div class="formitem">
|
<div class="formitem">
|
||||||
<button form="topic_create_form_form" name="topic-button" class="formbutton">Create Topic</button>
|
<button form="topic_create_form_form" name="topic-button" class="formbutton">Create Topic</button>
|
||||||
|
<button form="topic_create_form_form" class="formbutton" id="add_poll_button">Add Poll</button>
|
||||||
{{if .CurrentUser.Perms.UploadFiles}}
|
{{if .CurrentUser.Perms.UploadFiles}}
|
||||||
<input name="upload_files" form="topic_create_form_form" id="upload_files" multiple type="file" style="display: none;" />
|
<input name="upload_files" form="topic_create_form_form" id="upload_files" multiple type="file" style="display: none;" />
|
||||||
<label for="upload_files" class="formbutton add_file_button">Add File</label>
|
<label for="upload_files" class="formbutton add_file_button">Add File</label>
|
||||||
|
|
|
@ -74,9 +74,19 @@
|
||||||
<textarea form="topic_create_form_form" id="input_content" name="topic-content" placeholder="Insert post here" required></textarea>
|
<textarea form="topic_create_form_form" id="input_content" name="topic-content" placeholder="Insert post here" required></textarea>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="formrow poll_content_row auto_hide">
|
||||||
|
<div class="formitem">
|
||||||
|
<div class="pollinput">
|
||||||
|
<input type="checkbox" disabled />
|
||||||
|
<label class="pollinputlabel"></label>
|
||||||
|
<input type="text" placeholder="Add new poll option" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="formrow quick_button_row">
|
<div class="formrow quick_button_row">
|
||||||
<div class="formitem">
|
<div class="formitem">
|
||||||
<button form="topic_create_form_form" class="formbutton">Create Topic</button>
|
<button form="topic_create_form_form" class="formbutton">Create Topic</button>
|
||||||
|
<button form="topic_create_form_form" class="formbutton" id="add_poll_button">Add Poll</button>
|
||||||
{{if .CurrentUser.Perms.UploadFiles}}
|
{{if .CurrentUser.Perms.UploadFiles}}
|
||||||
<input name="upload_files" form="topic_create_form_form" id="upload_files" multiple type="file" style="display: none;" />
|
<input name="upload_files" form="topic_create_form_form" id="upload_files" multiple type="file" style="display: none;" />
|
||||||
<label for="upload_files" class="formbutton add_file_button">Add File</label>
|
<label for="upload_files" class="formbutton add_file_button">Add File</label>
|
||||||
|
|
|
@ -502,6 +502,20 @@ input, select {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
input[type=checkbox] {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
input[type=checkbox] + label {
|
||||||
|
width: 10px;
|
||||||
|
border: black;
|
||||||
|
background-color: var(--element-background-color);
|
||||||
|
}
|
||||||
|
.poll_content_row {
|
||||||
|
padding-left: 20px;
|
||||||
|
padding-top: 4px;
|
||||||
|
padding-bottom: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
.formbutton {
|
.formbutton {
|
||||||
margin-top: 12px;
|
margin-top: 12px;
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
|
@ -534,6 +548,9 @@ input, select {
|
||||||
margin-left: 10px;
|
margin-left: 10px;
|
||||||
margin-top: 8px;
|
margin-top: 8px;
|
||||||
}
|
}
|
||||||
|
.quick_button_row #add_poll_button {
|
||||||
|
background: hsl(209, 47%, 56%);
|
||||||
|
}
|
||||||
.quick_button_row .add_file_button {
|
.quick_button_row .add_file_button {
|
||||||
background: hsl(129, 57%, 56%);
|
background: hsl(129, 57%, 56%);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue