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