Documentation
¶
Overview ¶
Package models defines core data structures for go-pugleaf
Index ¶
- Constants
- Variables
- func BatchSanitizeArticles(articles []*Article)
- func BatchSanitizeOverviews(overviews []*Overview)
- func BatchSetCachedSanitized(articles map[string]map[string]template.HTML)
- func ClearNewsgroupCache()
- func ClearSanitizedCache()
- func ConvertToUTF8(text string) string
- func GetCachedSanitized(messageID string, field string) (template.HTML, bool)
- func GetCachedSanitizedArticle(messageID string) (*cache.SanitizedArticle, bool)
- func GetNewsgroupCache() *cache.NewsgroupCache
- func GetSanitizedCache() *cache.SanitizedCache
- func InitNewsgroupCache(maxEntries int, maxAge time.Duration)
- func InitSanitizedCache(maxEntries int, maxAge time.Duration)
- func PrintTimeSinceHumanReadable(t time.Time) string
- func SetCachedNewsgroups(page, pageSize int, groups []*Newsgroup, totalCount int)
- func SetCachedSanitized(messageID string, field string, value template.HTML)
- func SplitStringForLastSpace(s string) (string, string)
- type AIModel
- type APIToken
- type ActiveNewsgroup
- type Article
- type CacheKey
- type ForumThread
- type Hierarchy
- type NNTPSession
- type NNTPUser
- type Newsgroup
- type Overview
- type PaginatedResponse
- type PaginationInfo
- type Provider
- type Section
- type SectionGroup
- type Session
- type Setting
- type SiteNews
- type SpamTracking
- type Thread
- type User
- type UserPermission
Constants ¶
const ( StatusActive = "y" // Active - posting allowed StatusNoPost = "n" // No posting allowed StatusModerated = "m" // Moderated StatusDisabled = "x" // Disabled )
GroupStatus constants for ActiveNewsgroup
const DisableSanitizedCache = true
Global toggle to enable/disable the sanitized cache at compile-time. Set to true to completely bypass Get/Set operations for sanitized cache.
Variables ¶
var PostQueueChannel = make(chan *Article, 100)
PostQueueChannel is the global channel for articles posted from web interface This channel is used to pass articles from the web interface to the processor for background processing and insertion into the NNTP system
Functions ¶
func BatchSanitizeArticles ¶
func BatchSanitizeArticles(articles []*Article)
BatchSanitizeArticles pre-sanitizes and caches all commonly-used fields for multiple articles This should be called before rendering templates to avoid lock contention during template execution
func BatchSanitizeOverviews ¶
func BatchSanitizeOverviews(overviews []*Overview)
BatchSanitizeOverviews pre-sanitizes and caches all commonly-used fields for multiple overviews This should be called before rendering templates to avoid lock contention during template execution
func BatchSetCachedSanitized ¶
BatchSetCachedSanitized stores multiple complete sanitized articles in cache
func ClearNewsgroupCache ¶
func ClearNewsgroupCache()
ClearNewsgroupCache clears all cached newsgroup data
func ClearSanitizedCache ¶
func ClearSanitizedCache()
ClearSanitizedCache clears all cached sanitized article data This is useful when encoding logic changes and cached articles need to be re-processed
func ConvertToUTF8 ¶
ConvertToUTF8 converts text from Latin-1 to UTF-8 if needed, decodes MIME encoded-words and HTML entities This function is used for properly decoding newsgroup text content without HTML escaping
func GetCachedSanitized ¶
GetCachedSanitized retrieves a cached sanitized field value by message ID
func GetCachedSanitizedArticle ¶
func GetCachedSanitizedArticle(messageID string) (*cache.SanitizedArticle, bool)
GetCachedSanitizedArticle retrieves a complete cached sanitized article
func GetNewsgroupCache ¶
func GetNewsgroupCache() *cache.NewsgroupCache
GetNewsgroupCache returns the global newsgroup cache instance
func GetSanitizedCache ¶
func GetSanitizedCache() *cache.SanitizedCache
GetSanitizedCache returns the global sanitized cache instance
func InitNewsgroupCache ¶
InitNewsgroupCache initializes the global newsgroup cache
func InitSanitizedCache ¶
InitSanitizedCache initializes the global sanitized cache
func SetCachedNewsgroups ¶
SetCachedNewsgroups stores newsgroup data in cache
func SetCachedSanitized ¶
SetCachedSanitized stores a sanitized field value in cache by message ID
func SplitStringForLastSpace ¶
Types ¶
type AIModel ¶
type AIModel struct {
ID int `json:"id" db:"id"`
PostKey string `json:"post_key" db:"post_key"` // Key used in frontend forms (e.g. "gemma3_12b")
OllamaModelName string `json:"ollama_model_name" db:"ollama_model_name"` // Real Ollama model name (e.g. "gemma3:12b")
DisplayName string `json:"display_name" db:"display_name"` // User-friendly name (e.g. "Gemma 3 12B")
Description string `json:"description" db:"description"` // Short description for users
IsActive bool `json:"is_active" db:"is_active"` // Admin can enable/disable
IsDefault bool `json:"is_default" db:"is_default"` // Default selection for new chats
SortOrder int `json:"sort_order" db:"sort_order"` // Display order in UI
CreatedAt time.Time `json:"created_at" db:"created_at"`
UpdatedAt time.Time `json:"updated_at" db:"updated_at"`
}
AIModel represents an AI model configuration for chat functionality
type APIToken ¶
type APIToken struct {
ID int `json:"id" db:"id"`
APIToken string `json:"apitoken" db:"apitoken"` // Unique token string
OwnerName string `json:"ownername" db:"ownername"` // Name of the owner (system or user)
OwnerID int `json:"ownerid" db:"ownerid"` // 0 for system tokens, >0 for user IDs
CreatedAt time.Time `json:"created_at" db:"created_at"`
LastUsedAt time.Time `json:"last_used_at" db:"last_used_at"`
ExpiresAt time.Time `json:"expires_at" db:"expires_at"`
IsEnabled bool `json:"is_enabled" db:"is_enabled"` // Administrative control
UsageCount int `json:"usage_count" db:"usage_count"` // Number of times used
}
type ActiveNewsgroup ¶
type ActiveNewsgroup struct {
GroupID int `db:"group_id" json:"group_id"`
GroupName string `db:"group_name" json:"group_name"`
Description string `db:"description" json:"description"`
HighWater int `db:"high_water" json:"high_water"`
LowWater int `db:"low_water" json:"low_water"`
MessageCount int `db:"message_count" json:"message_count"`
Status string `db:"status" json:"status"`
CreatedAt time.Time `db:"created_at" json:"created_at"`
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
}
ActiveNewsgroup represents a newsgroup in the active.db
type Article ¶
type Article struct {
GetDataFunc func(what string, group string) string `json:"-" db:"-"`
Mux sync.RWMutex `json:"-" db:"-"`
MessageID string `json:"message_id" db:"message_id"`
Subject string `json:"subject" db:"subject"`
FromHeader string `json:"from_header" db:"from_header"`
DateSent time.Time `json:"date_sent" db:"date_sent"`
DateString string `json:"date_string" db:"date_string"`
References string `json:"references" db:"references"`
Bytes int `json:"bytes" db:"bytes"`
Lines int `json:"lines" db:"lines"`
ReplyCount int `json:"reply_count" db:"reply_count"`
HeadersJSON string `json:"headers_json" db:"headers_json"`
BodyText string `json:"body_text" db:"body_text"`
Path string `json:"path" db:"path"` // headers network path
ImportedAt time.Time `json:"imported_at" db:"imported_at"`
Spam int `json:"spam" db:"spam"` // Simple spam counter
Hide int `json:"hide" db:"hide"` // Simple hide counter
Sanitized bool `json:"-" db:"-"`
MsgIdItem *history.MessageIdItem `json:"-" db:"-"` // Cached MessageIdItem for history lookups
// Temporary fields for parsing - not stored in database
Headers map[string][]string `json:"-" db:"-"` // Raw headers during parsing
ArticleNums map[*string]int64 `json:"-" db:"-"` // key is newsgroup pointer, value is article number
NNTPhead []string `json:"-" db:"-"` // used for peering
NNTPbody []string `json:"-" db:"-"` // used for peering
IsThrRoot bool `json:"-" db:"-"` // used in db_batch
IsReply bool `json:"-" db:"-"` // used in db_batch
RefSlice []string `json:"-" db:"-"` // Parsed references for threading
NewsgroupsPtr []*string `json:"-" db:"-"` // Parsed newsgroup for threading
ProcessQueue chan *string `json:"-" db:"-"` // newsgroup ptr for batching
}
Article represents a newsgroup article (per-group DB)
func (*Article) GetCleanSubject ¶
GetCleanSubject returns the article subject text decoded and cleaned but without HTML escaping This is suitable for use in browser titles where HTML entities should not be displayed literally
func (*Article) PrintSanitized ¶
Article PrintSanitized returns UTF-8 converted and HTML-escaped text safe for web display This method now primarily relies on pre-cached values from BatchSanitizeArticles groupName parameter is optional for caching - if empty, caching is skipped
func (*Article) StripDangerousHTML ¶
func (a *Article) StripDangerousHTML()
StripDangerousHTML removes potentially dangerous HTML/script content from all user-facing fields
type ForumThread ¶
type ForumThread struct {
RootArticle *Overview `json:"thread_root"` // The original post
Replies []*Overview `json:"replies"` // All replies in flat list
MessageCount int `json:"message_count"` // Total messages in thread
LastActivity time.Time `json:"last_activity"` // Most recent activity
}
ForumThread represents a complete thread with root article and replies
func (*ForumThread) PrintLastActivity ¶
func (f *ForumThread) PrintLastActivity() string
type Hierarchy ¶
type Hierarchy struct {
ID int64 `json:"id" db:"id"`
Name string `json:"name" db:"name"`
Description string `json:"description" db:"description"`
GroupCount int `json:"group_count" db:"group_count"`
LastUpdated time.Time `json:"last_updated" db:"last_updated"`
CreatedAt time.Time `json:"created_at" db:"created_at"`
}
Hierarchy represents a Usenet hierarchy (e.g., comp, alt, rec)
type NNTPSession ¶
type NNTPSession struct {
ID int `json:"id" db:"id"`
UserID int `json:"user_id" db:"user_id"`
ConnectionID string `json:"connection_id" db:"connection_id"`
RemoteAddr string `json:"remote_addr" db:"remote_addr"`
StartedAt time.Time `json:"started_at" db:"started_at"`
LastActivity time.Time `json:"last_activity" db:"last_activity"`
IsActive bool `json:"is_active" db:"is_active"`
}
NNTPSession represents an active NNTP connection session
type NNTPUser ¶
type NNTPUser struct {
ID int `json:"id" db:"id"`
Username string `json:"username" db:"username"`
Password string `json:"password" db:"password"` // Plain text or hashed, depending on implementation
MaxConns int `json:"maxconns" db:"maxconns"`
Posting bool `json:"posting" db:"posting"`
WebUserID int64 `json:"web_user_id" db:"web_user_id"` // Optional mapping to web user (0 = no mapping)
CreatedAt time.Time `json:"created_at" db:"created_at"`
UpdatedAt time.Time `json:"updated_at" db:"updated_at"`
LastLogin *time.Time `json:"last_login" db:"last_login"`
IsActive bool `json:"is_active" db:"is_active"`
}
NNTPUser represents an NNTP-specific user account for newsreader clients
type Newsgroup ¶
type Newsgroup struct {
ID int64 `json:"id" db:"id"`
Name string `json:"name" db:"name"`
Active bool `json:"active" db:"active"`
Description string `json:"description" db:"description"`
LastArticle int64 `json:"last_article" db:"last_article"`
MessageCount int64 `json:"message_count" db:"message_count"`
ExpiryDays int `json:"expiry_days" db:"expiry_days"`
MaxArticles int `json:"max_articles" db:"max_articles"`
MaxArtSize int `json:"max_art_size" db:"max_art_size"`
Hierarchy string `json:"hierarchy" db:"hierarchy"` // Extracted hierarchy for efficient queries
// NNTP-specific fields
HighWater int `json:"high_water" db:"high_water"`
LowWater int `json:"low_water" db:"low_water"`
Status string `json:"status" db:"status"`
CreatedAt time.Time `json:"created_at" db:"created_at"`
UpdatedAt time.Time `json:"updated_at" db:"updated_at"`
}
Newsgroup represents a subscribed newsgroup
func GetCachedNewsgroups ¶
GetCachedNewsgroups retrieves cached newsgroup data
func (*Newsgroup) PrintLastActivity ¶
PrintLastActivity returns a human-readable time difference from now for newsgroups
type Overview ¶
type Overview struct {
ArticleNum int64 `json:"article_num" db:"article_num"`
Subject string `json:"subject" db:"subject"`
FromHeader string `json:"from_header" db:"from_header"`
DateSent time.Time `json:"date_sent" db:"date_sent"`
DateString string `json:"date_string" db:"date_string"`
MessageID string `json:"message_id" db:"message_id"`
References string `json:"references" db:"references"`
Bytes int `json:"bytes" db:"bytes"`
Lines int `json:"lines" db:"lines"`
ReplyCount int `json:"reply_count" db:"reply_count"`
Downloaded int `json:"downloaded" db:"downloaded"` // 0 = not downloaded, 1 = downloaded
Spam int `json:"spam" db:"spam"` // Spam flag counter
Hide int `json:"hide" db:"hide"` // Hide flag counter
Sanitized bool `json:"-" db:"-"`
ArticleNums map[*string]int64 `json:"-" db:"-"` // key is newsgroup pointer, value is article number
}
Overview represents XOVER data for fast group listing downloaded: 0 = not downloaded, 1 = downloaded Add 'Downloaded' field for tracking download status Add Sanitized flag for consistent sanitization
func (*Overview) GetCleanSubject ¶
GetCleanSubject returns the overview subject text decoded and cleaned but without HTML escaping This is suitable for use in browser titles where HTML entities should not be displayed literally
func (*Overview) PrintSanitized ¶
Overview PrintSanitized returns a sanitized version of the specified field for web display groupName parameter is optional for caching - if empty, caching is skipped
func (*Overview) ReferenceCount ¶
ReferenceCount returns the number of references in the References field This represents how many articles this one is replying to, not how many replies it has
type PaginatedResponse ¶
type PaginatedResponse struct {
Data interface{} `json:"data"`
Page int `json:"page"`
PageSize int `json:"page_size"`
TotalCount int `json:"total_count"`
TotalPages int `json:"total_pages"`
HasNext bool `json:"has_next"`
HasPrev bool `json:"has_prev"`
}
PaginatedResponse represents a paginated API response
type PaginationInfo ¶
type PaginationInfo struct {
CurrentPage int
PageSize int
TotalCount int
TotalPages int
HasNext bool
HasPrev bool
NextPage int
PrevPage int
}
PaginationInfo represents pagination information for templates
func NewPaginationInfo ¶
func NewPaginationInfo(page, pageSize, totalCount int) *PaginationInfo
NewPaginationInfo creates pagination info
type Provider ¶
type Provider struct {
ID int64 `json:"id" db:"id"`
Enabled bool `json:"enabled" db:"enabled"` // Whether this provider is enabled
Priority int `json:"priority" db:"priority"` // Priority for load balancing
Grp string `json:"grp" db:"grp"`
Name string `json:"name" db:"name"`
Host string `json:"host" db:"host"`
Port int `json:"port" db:"port"`
SSL bool `json:"ssl" db:"ssl"`
Username string `json:"username" db:"username"`
Password string `json:"password" db:"password"`
MaxConns int `json:"max_conns" db:"max_conns"` // Maximum concurrent connections
MaxArtSize int `json:"max_art_size" db:"max_art_size"` // Maximum article size in bytes
Posting bool `json:"posting" db:"posting"` // Whether posting is enabled for this provider
// Proxy configuration fields
ProxyEnabled bool `json:"proxy_enabled" db:"proxy_enabled"` // Whether to use proxy for this provider
ProxyType string `json:"proxy_type" db:"proxy_type"` // Proxy type: socks4, socks5
ProxyHost string `json:"proxy_host" db:"proxy_host"` // Proxy server hostname/IP
ProxyPort int `json:"proxy_port" db:"proxy_port"` // Proxy server port
ProxyUsername string `json:"proxy_username" db:"proxy_username"` // Proxy authentication username
ProxyPassword string `json:"proxy_password" db:"proxy_password"` // Proxy authentication password
CreatedAt time.Time `json:"created_at" db:"created_at"`
}
Provider represents an NNTP provider configuration
type Section ¶
type Section struct {
ID int `json:"id" db:"id"`
Name string `json:"name" db:"name"`
DisplayName string `json:"display_name" db:"display_name"`
Description string `json:"description" db:"description"`
ShowInHeader bool `json:"show_in_header" db:"show_in_header"`
EnableLocalSpool bool `json:"enable_local_spool" db:"enable_local_spool"`
SortOrder int `json:"sort_order" db:"sort_order"`
CreatedAt time.Time `json:"created_at" db:"created_at"`
GroupCount int `json:"group_count" db:"group_count"` // Number of newsgroups assigned to this section
}
Section represents a RockSolid Light section (from menu.conf)
type SectionGroup ¶
type SectionGroup struct {
ID int `json:"id" db:"id"`
SectionID int `json:"section_id" db:"section_id"`
NewsgroupName string `json:"newsgroup_name" db:"newsgroup_name"`
GroupDescription string `json:"group_description" db:"group_description"`
SortOrder int `json:"sort_order" db:"sort_order"`
IsCategoryHeader bool `json:"is_category_header" db:"is_category_header"`
CreatedAt time.Time `json:"created_at" db:"created_at"`
}
SectionGroup represents a newsgroup assigned to a section
type Session ¶
type Session struct {
ID string `json:"id" db:"id"`
UserID int64 `json:"user_id" db:"user_id"`
CreatedAt time.Time `json:"created_at" db:"created_at"`
ExpiresAt time.Time `json:"expires_at" db:"expires_at"`
}
Session represents a user session
type SiteNews ¶
type SiteNews struct {
ID int `json:"id" db:"id"`
Subject string `json:"subject" db:"subject"`
Content string `json:"content" db:"content"`
DatePublished time.Time `json:"date_published" db:"date_published"`
IsVisible bool `json:"is_visible" db:"is_visible"`
CreatedAt time.Time `json:"created_at" db:"created_at"`
UpdatedAt time.Time `json:"updated_at" db:"updated_at"`
}
SiteNews represents a site news item
type SpamTracking ¶
type SpamTracking struct {
ID int `json:"id" db:"id"`
NewsgroupID int `json:"newsgroup_id" db:"newsgroup_id"`
ArticleNum int64 `json:"article_num" db:"article_num"`
}
SpamTracking represents spam tracking for an article in the main database
type Thread ¶
type Thread struct {
ID int `json:"id" db:"id"`
RootArticle int64 `json:"root_article" db:"root_article"`
ParentArticle *int64 `json:"parent_article" db:"parent_article"` // Pointer for NULL values
ChildArticle int64 `json:"child_article" db:"child_article"`
Depth int `json:"depth" db:"depth"`
ThreadOrder int `json:"thread_order" db:"thread_order"`
}
Thread represents a parent/child relationship for threading
type User ¶
type User struct {
ID int64 `json:"id" db:"id"`
Username string `json:"username" db:"username"`
Email string `json:"email" db:"email"`
PasswordHash string `json:"password_hash" db:"password_hash"`
DisplayName string `json:"display_name" db:"display_name"`
SessionID string `json:"session_id" db:"session_id"` // Current active session (64 chars)
LastLoginIP string `json:"last_login_ip" db:"last_login_ip"` // IP of last login (for logging only)
SessionExpiresAt *time.Time `json:"session_expires_at" db:"session_expires_at"` // Session expiration (sliding)
LoginAttempts int `json:"login_attempts" db:"login_attempts"` // Failed login attempts counter
CreatedAt time.Time `json:"created_at" db:"created_at"`
UpdatedAt time.Time `json:"updated_at" db:"updated_at"`
}
User represents a web user account