Documentation
¶
Overview ¶
Package models defines core data structures for go-pugleaf
Index ¶
- Constants
- 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 ¶
This section is empty.
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 int `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 int `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 int `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
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