Previously constrained to 18 chars (old PDS restriction), now uses the full 63-char label limit. Preserves full hostname in handles (e.g., calmchristmas-substack-com) with shortening only when needed. Removes PublishAccount field from Feed struct in favor of runtime derivation. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
97 lines
3.2 KiB
Go
97 lines
3.2 KiB
Go
package commons
|
|
|
|
import (
|
|
"time"
|
|
)
|
|
|
|
// Feed represents a discovered RSS/Atom feed with metadata
|
|
type Feed struct {
|
|
URL string `json:"url"`
|
|
Type string `json:"type"` // "rss", "atom", "json", or "unknown"
|
|
Title string `json:"title,omitempty"`
|
|
Description string `json:"description,omitempty"`
|
|
Language string `json:"language,omitempty"`
|
|
|
|
// Timing
|
|
LastCheckedAt time.Time `json:"last_checked_at,omitempty"` // feed_check: when last checked
|
|
|
|
// Cache headers for conditional requests
|
|
ETag string `json:"etag,omitempty"`
|
|
LastModified string `json:"last_modified,omitempty"`
|
|
|
|
// Status: PUBLISH, STANDBY, IGNORE
|
|
Status string `json:"status"`
|
|
LastError string `json:"last_error,omitempty"`
|
|
|
|
// Computed from items table (not stored in feeds table)
|
|
ItemCount int `json:"item_count,omitempty"`
|
|
|
|
// Adaptive check interval
|
|
MissCount int `json:"miss_count"` // Consecutive checks with no new items
|
|
}
|
|
|
|
// Enclosure represents a media attachment (audio, video, image)
|
|
type Enclosure struct {
|
|
URL string `json:"url"`
|
|
Type string `json:"type"` // MIME type (audio/mpeg, image/jpeg, etc.)
|
|
}
|
|
|
|
// Item represents an individual entry/article from a feed
|
|
type Item struct {
|
|
FeedURL string `json:"feed_url"`
|
|
Link string `json:"link"` // Primary key (with feed_url)
|
|
Title string `json:"title,omitempty"`
|
|
Description string `json:"description,omitempty"`
|
|
Author string `json:"author,omitempty"`
|
|
PubDate time.Time `json:"pub_date,omitempty"`
|
|
|
|
// Media attachments
|
|
Enclosure *Enclosure `json:"enclosure,omitempty"` // Primary enclosure (podcast audio, etc.)
|
|
ImageURLs []string `json:"image_urls,omitempty"` // Image URLs extracted from description
|
|
Tags []string `json:"tags,omitempty"` // Category/tag strings from feed
|
|
|
|
}
|
|
|
|
// ShortURL represents a shortened URL mapping
|
|
type ShortURL struct {
|
|
Code string `json:"code"`
|
|
OriginalURL string `json:"original_url"`
|
|
ItemGUID string `json:"item_guid,omitempty"`
|
|
FeedURL string `json:"feed_url,omitempty"`
|
|
CreatedAt time.Time `json:"created_at"`
|
|
ClickCount int `json:"click_count"`
|
|
}
|
|
|
|
// Click represents a click event on a short URL
|
|
type Click struct {
|
|
ID int64 `json:"id"`
|
|
ShortCode string `json:"short_code"`
|
|
ClickedAt time.Time `json:"clicked_at"`
|
|
Referrer string `json:"referrer,omitempty"`
|
|
UserAgent string `json:"user_agent,omitempty"`
|
|
IPHash string `json:"ip_hash,omitempty"`
|
|
Country string `json:"country,omitempty"`
|
|
}
|
|
|
|
// DashboardStats holds all statistics for the dashboard
|
|
type DashboardStats struct {
|
|
// Feed stats by status
|
|
TotalFeeds int `json:"total_feeds"`
|
|
PublishFeeds int `json:"publish_feeds"` // status='PUBLISH'
|
|
StandbyFeeds int `json:"standby_feeds"` // status='STANDBY'
|
|
IgnoreFeeds int `json:"ignore_feeds"` // status='IGNORE'
|
|
|
|
// Feed stats by type
|
|
RSSFeeds int `json:"rss_feeds"`
|
|
AtomFeeds int `json:"atom_feeds"`
|
|
JSONFeeds int `json:"json_feeds"`
|
|
UnknownFeeds int `json:"unknown_feeds"`
|
|
|
|
// Processing rates (per minute)
|
|
FeedCheckRate int `json:"feed_check_rate"` // feed_check per minute
|
|
|
|
// Timing
|
|
UpdatedAt time.Time `json:"updated_at"`
|
|
}
|
|
|