336 lines
10 KiB
Go
336 lines
10 KiB
Go
|
//
|
||
|
// database.go
|
||
|
//
|
||
|
package srnd
|
||
|
|
||
|
import (
|
||
|
"log"
|
||
|
"net"
|
||
|
"strings"
|
||
|
"time"
|
||
|
)
|
||
|
|
||
|
// a ( MessageID , newsgroup ) tuple
|
||
|
type ArticleEntry [2]string
|
||
|
|
||
|
func (self ArticleEntry) Newsgroup() string {
|
||
|
return self[1]
|
||
|
}
|
||
|
|
||
|
func (self ArticleEntry) MessageID() string {
|
||
|
return self[0]
|
||
|
}
|
||
|
|
||
|
// a (messageID , parent messageID) tuple
|
||
|
type MessageIDTuple [2]string
|
||
|
|
||
|
func (self MessageIDTuple) MessageID() string {
|
||
|
return strings.Trim(self[0], " ")
|
||
|
}
|
||
|
|
||
|
func (self MessageIDTuple) Reference() string {
|
||
|
r := strings.Trim(self[1], " ")
|
||
|
if len(r) == 0 {
|
||
|
return self.MessageID()
|
||
|
}
|
||
|
return r
|
||
|
}
|
||
|
|
||
|
// a ( time point, magnitude ) tuple
|
||
|
type PostEntry [2]int64
|
||
|
|
||
|
func (self PostEntry) Time() time.Time {
|
||
|
return time.Unix(self[0], 0)
|
||
|
}
|
||
|
|
||
|
func (self PostEntry) Count() int64 {
|
||
|
return self[1]
|
||
|
}
|
||
|
|
||
|
// stats about newsgroup postings
|
||
|
type NewsgroupStats struct {
|
||
|
Posted []PostEntry
|
||
|
Delted []PostEntry
|
||
|
Hits []PostEntry
|
||
|
Start time.Time
|
||
|
End time.Time
|
||
|
Name string
|
||
|
}
|
||
|
|
||
|
type PostingStatsEntry struct {
|
||
|
Groups []NewsgroupStats
|
||
|
}
|
||
|
|
||
|
type PostingStats struct {
|
||
|
History []PostingStatsEntry
|
||
|
}
|
||
|
|
||
|
type Database interface {
|
||
|
Close()
|
||
|
CreateTables()
|
||
|
HasNewsgroup(group string) bool
|
||
|
HasArticle(message_id string) bool
|
||
|
HasArticleLocal(message_id string) bool
|
||
|
RegisterNewsgroup(group string)
|
||
|
RegisterArticle(article NNTPMessage) error
|
||
|
GetAllArticlesInGroup(group string, send chan ArticleEntry)
|
||
|
CountAllArticlesInGroup(group string) (int64, error)
|
||
|
GetAllArticles() []ArticleEntry
|
||
|
|
||
|
SetConnectionLifetime(seconds int)
|
||
|
SetMaxOpenConns(n int)
|
||
|
SetMaxIdleConns(n int)
|
||
|
|
||
|
// check if a newsgroup is banned
|
||
|
NewsgroupBanned(group string) (bool, error)
|
||
|
|
||
|
// ban / unban newsgroup
|
||
|
BanNewsgroup(group string) error
|
||
|
UnbanNewsgroup(group string) error
|
||
|
|
||
|
// delete an entire newsgroup
|
||
|
// delete from the article store too
|
||
|
NukeNewsgroup(group string, store ArticleStore)
|
||
|
|
||
|
// return true if this is root post has expired
|
||
|
IsExpired(root_message_id string) bool
|
||
|
|
||
|
// get an article's MessageID given the hash of the MessageID
|
||
|
// return an article entry or nil when it doesn't exist + and error if it happened
|
||
|
GetMessageIDByHash(hash string) (ArticleEntry, error)
|
||
|
|
||
|
// get root message_id, newsgroup, pageno for a post regardless if it's rootpost or not
|
||
|
GetInfoForMessage(msgid string) (string, string, int64, error)
|
||
|
|
||
|
// what page is the thread with this root post on?
|
||
|
// return newsgroup, pageno
|
||
|
GetPageForRootMessage(root_message_id string) (string, int64, error)
|
||
|
|
||
|
// record that a message given a message id was posted signed by this pubkey
|
||
|
RegisterSigned(message_id, pubkey string) error
|
||
|
|
||
|
// get the number of articles we have
|
||
|
ArticleCount() int64
|
||
|
|
||
|
// return true if this thread has any replies
|
||
|
ThreadHasReplies(root_message_id string) bool
|
||
|
|
||
|
// get the number of posts in a certain newsgroup since N seconds ago
|
||
|
// if N <= 0 then count all we have now
|
||
|
CountPostsInGroup(group string, time_frame int64) int64
|
||
|
|
||
|
// get all replies to a thread
|
||
|
// if last > 0 then get that many of the last replies
|
||
|
// start at reply number start
|
||
|
GetThreadReplies(root_message_id string, start, last int) []string
|
||
|
|
||
|
// count the number of replies to this thread
|
||
|
CountThreadReplies(root_message_id string) int64
|
||
|
|
||
|
// get all attachments for this message
|
||
|
GetPostAttachments(message_id string) []string
|
||
|
|
||
|
// get all attachments for this message
|
||
|
GetPostAttachmentModels(prefix, message_id string) []AttachmentModel
|
||
|
|
||
|
// return true if this newsgroup has posts
|
||
|
GroupHasPosts(newsgroup string) bool
|
||
|
|
||
|
// get all active threads on a board
|
||
|
// send each thread's ArticleEntry down a channel
|
||
|
GetGroupThreads(newsgroup string, send chan ArticleEntry)
|
||
|
|
||
|
// get every message id for root posts that need to be expired in a newsgroup
|
||
|
// threadcount is the upperbound limit to how many root posts we keep
|
||
|
GetRootPostsForExpiration(newsgroup string, threadcount int) []string
|
||
|
|
||
|
// get the number of pages a board has
|
||
|
GetGroupPageCount(newsgroup string) int64
|
||
|
|
||
|
// get board page number N
|
||
|
// prefix and frontend are injected
|
||
|
// does not load replies for thread, only gets root posts
|
||
|
GetGroupForPage(prefix, frontend, newsgroup string, pageno, perpage int) BoardModel
|
||
|
|
||
|
// get the root posts of the last N bumped threads in a given newsgroup or "" for ukko
|
||
|
GetLastBumpedThreads(newsgroup string, threadcount int) []ArticleEntry
|
||
|
|
||
|
// get root posts of last N bumped threads with pagination offset
|
||
|
GetLastBumpedThreadsPaginated(newsgroup string, threadcount, offset int) []ArticleEntry
|
||
|
|
||
|
// get the PostModels for replies to a thread
|
||
|
// prefix is injected into the post models
|
||
|
GetThreadReplyPostModels(prefix, rootMessageID string, start, limit int) []PostModel
|
||
|
|
||
|
// get a post model for a post
|
||
|
// prefix is injected into the post model
|
||
|
GetPostModel(prefix, messageID string) PostModel
|
||
|
|
||
|
// add a public key to the database
|
||
|
AddModPubkey(pubkey string) error
|
||
|
|
||
|
// mark that a mod with this pubkey can act on all boards
|
||
|
MarkModPubkeyGlobal(pubkey string) error
|
||
|
|
||
|
// revoke mod with this pubkey the privilege of being able to act on all boards
|
||
|
UnMarkModPubkeyGlobal(pubkey string) error
|
||
|
|
||
|
// check if this mod pubkey can moderate at a global level
|
||
|
CheckModPubkeyGlobal(pubkey string) bool
|
||
|
|
||
|
// check if a mod with this pubkey has permission to moderate at all
|
||
|
CheckModPubkey(pubkey string) bool
|
||
|
|
||
|
// check if a pubkey has admin privs
|
||
|
CheckAdminPubkey(pubkey string) (bool, error)
|
||
|
|
||
|
// mark a key as having admin privs
|
||
|
MarkPubkeyAdmin(pubkey string) error
|
||
|
|
||
|
// unmark a key as having admin privs
|
||
|
UnmarkPubkeyAdmin(pubkey string) error
|
||
|
|
||
|
// check if a mod with this pubkey can moderate on the given newsgroup
|
||
|
CheckModPubkeyCanModGroup(pubkey, newsgroup string) bool
|
||
|
|
||
|
// add a pubkey to be able to mod a newsgroup
|
||
|
MarkModPubkeyCanModGroup(pubkey, newsgroup string) error
|
||
|
|
||
|
// remote a pubkey to they can't mod a newsgroup
|
||
|
UnMarkModPubkeyCanModGroup(pubkey, newsgroup string) error
|
||
|
|
||
|
// ban an article
|
||
|
BanArticle(messageID, reason string) error
|
||
|
|
||
|
// check if an article is banned or not
|
||
|
ArticleBanned(messageID string) bool
|
||
|
|
||
|
// Get ip address given the encrypted version
|
||
|
// return emtpy string if we don't have it
|
||
|
GetIPAddress(encAddr string) (string, error)
|
||
|
|
||
|
// check if an ip is banned from our local
|
||
|
CheckIPBanned(addr string) (bool, error)
|
||
|
|
||
|
// check if an encrypted ip is banned from our local
|
||
|
CheckEncIPBanned(encAddr string) (bool, error)
|
||
|
|
||
|
// ban an ip address from the local
|
||
|
BanAddr(addr string) error
|
||
|
|
||
|
// unban an ip address from the local
|
||
|
UnbanAddr(addr string) error
|
||
|
|
||
|
// ban an encrypted ip address from the remote
|
||
|
BanEncAddr(encAddr string) error
|
||
|
|
||
|
// return the encrypted version of an IPAddress
|
||
|
// if it's not already there insert it into the database
|
||
|
GetEncAddress(addr string) (string, error)
|
||
|
|
||
|
// get the decryption key for an encrypted address
|
||
|
// return empty string if we don't have it
|
||
|
GetEncKey(encAddr string) (string, error)
|
||
|
|
||
|
// delete an article from the database
|
||
|
DeleteArticle(msg_id string) error
|
||
|
|
||
|
// detele the existance of a thread from the threads table, does NOT remove replies
|
||
|
DeleteThread(root_msg_id string) error
|
||
|
|
||
|
// get threads per page for a newsgroup
|
||
|
GetThreadsPerPage(group string) (int, error)
|
||
|
|
||
|
// get pages per board for a newsgroup
|
||
|
GetPagesPerBoard(group string) (int, error)
|
||
|
|
||
|
// get every newsgroup we know of
|
||
|
GetAllNewsgroups() []string
|
||
|
|
||
|
// get all post models in a newsgroup
|
||
|
// ordered from oldest to newest
|
||
|
GetPostsInGroup(group string) ([]PostModel, error)
|
||
|
|
||
|
// get the numerical id of the last , first article for a given group
|
||
|
GetLastAndFirstForGroup(group string) (int64, int64, error)
|
||
|
|
||
|
// get a message id give a newsgroup and the nntp id
|
||
|
GetMessageIDForNNTPID(group string, id int64) (string, error)
|
||
|
|
||
|
// get nntp id for a given message-id
|
||
|
GetNNTPIDForMessageID(group, msgid string) (int64, error)
|
||
|
|
||
|
// get the last N days post count in decending order
|
||
|
GetLastDaysPosts(n int64) []PostEntry
|
||
|
|
||
|
// get the last N days post count in decending order
|
||
|
GetLastDaysPostsForGroup(newsgroup string, n int64) []PostEntry
|
||
|
|
||
|
// get post history per month since beginning of time
|
||
|
GetMonthlyPostHistory() []PostEntry
|
||
|
|
||
|
// get the last N posts that were made globally
|
||
|
GetLastPostedPostModels(prefix string, n int64) []PostModel
|
||
|
|
||
|
// check if an nntp login cred is correct
|
||
|
CheckNNTPLogin(username, passwd string) (bool, error)
|
||
|
|
||
|
// add an nntp login credential
|
||
|
AddNNTPLogin(username, passwd string) error
|
||
|
|
||
|
// remove an nntp login credential
|
||
|
RemoveNNTPLogin(username string) error
|
||
|
|
||
|
// check if an nntp login credential given a user exists
|
||
|
CheckNNTPUserExists(username string) (bool, error)
|
||
|
|
||
|
// get the message ids of an article that has this header with the given value
|
||
|
GetMessageIDByHeader(name, value string) ([]string, error)
|
||
|
|
||
|
// get the headers for a message given its message-id
|
||
|
GetHeadersForMessage(msgid string) (ArticleHeaders, error)
|
||
|
|
||
|
// get all message-ids posted by posters in this cidr
|
||
|
GetMessageIDByCIDR(cidr *net.IPNet) ([]string, error)
|
||
|
|
||
|
// get all message-ids posted by poster with encrypted ip
|
||
|
GetMessageIDByEncryptedIP(encaddr string) ([]string, error)
|
||
|
|
||
|
// check if this public key is banned from posting
|
||
|
PubkeyIsBanned(pubkey string) (bool, error)
|
||
|
|
||
|
// ban a public key from posting
|
||
|
BanPubkey(pubkey string) error
|
||
|
|
||
|
// get all message-id posted before a time
|
||
|
GetPostsBefore(t time.Time) ([]string, error)
|
||
|
|
||
|
// get statistics about posting in a time slice
|
||
|
GetPostingStats(granularity, begin, end int64) (PostingStats, error)
|
||
|
|
||
|
// peform search query
|
||
|
SearchQuery(prefix, group, text string, chnl chan PostModel) error
|
||
|
|
||
|
// find posts with similar hash
|
||
|
SearchByHash(prefix, group, posthash string, chnl chan PostModel) error
|
||
|
|
||
|
// get full thread model
|
||
|
GetThreadModel(prefix, root_msgid string) (ThreadModel, error)
|
||
|
|
||
|
// get post models with nntp id in a newsgroup
|
||
|
GetNNTPPostsInGroup(newsgroup string) ([]PostModel, error)
|
||
|
|
||
|
// get post message-id where hash is similar to string
|
||
|
GetCitesByPostHashLike(like string) ([]MessageIDTuple, error)
|
||
|
}
|
||
|
|
||
|
func NewDatabase(db_type, schema, host, port, user, password string) Database {
|
||
|
if db_type == "postgres" {
|
||
|
if schema == "srnd" {
|
||
|
return NewPostgresDatabase(host, port, user, password)
|
||
|
}
|
||
|
}
|
||
|
log.Fatalf("invalid database type: %s/%s", db_type, schema)
|
||
|
return nil
|
||
|
}
|