v2 – zgo.at/goatcounter/v2 Index | Files | Directories

package goatcounter

import "zgo.at/goatcounter/v2"

Index

Constants

const (
	APIPermNothing    zint.Bitflag64 = 1 << iota
	APIPermCount                     // 2
	APIPermExport                    // 4
	APIPermSiteRead                  // 8
	APIPermSiteCreate                // 16
	APIPermSiteUpdate                // 32
	APIPermStats                     // 64
)

APIToken permissions.

DO NOT change the values of these constants; they're stored in the database.

const (
	StateActive  = "a"
	StateRequest = "r"
	StateDeleted = "d"
)

State column values.

const (
	CollectNothing        zint.Bitflag16 = 1 << iota
	CollectReferrer                      // 2
	CollectUserAgent                     // 4
	CollectScreenSize                    // 8
	CollectLocation                      // 16
	CollectLocationRegion                // 32
	CollectLanguage                      // 64
	CollectSession                       // 128
)

Settings.Collect values (bitmask)

DO NOT change the values of these constants; they're stored in the database.

Note: also update CollectFlags() method below.

Nothing is 1 and 0 is "unset". This is so we can distinguish between "this field was never sent in the form" vs. "user unchecked all boxes".

const (
	EmailReportNever = iota // Email once after 2 weeks; for new sites.
	EmailReportDaily
	EmailReportWeekly
	EmailReportBiWeekly
	EmailReportMonthly
)

UserSettings.EmailReport values.

const ExportVersion = "2"
const PathTotals = "TOTAL "

PathTotals is a special path to indicate this is the "total" overview.

Trailing whitespace is trimmed on paths, so this should never conflict.

Variables

var (
	// Valid UUID for testing: 00112233-4455-6677-8899-aabbccddeeff
	TestSession    = zint.Uint128{0x11223344556677, 0x8899aabbccddeeff}
	TestSeqSession = zint.Uint128{TestSession[0], TestSession[1] + 1}
)
var (
	RefSchemeHTTP      = ztype.Ptr("h")
	RefSchemeOther     = ztype.Ptr("o")
	RefSchemeGenerated = ztype.Ptr("g")
	RefSchemeCampaign  = ztype.Ptr("c")
)

ref_scheme column

var DB embed.FS

DB contains all files in db/*

var EmailReports = []int{EmailReportNever, EmailReportDaily, EmailReportWeekly,
	EmailReportBiWeekly, EmailReportMonthly}
var GeoDB []byte

GeoDB contains the GeoIP countries database.

var Memstore ms
var SQLiteHook = func(c *sqlite3.SQLiteConn) error {
	return c.RegisterFunc("percent_diff", func(start, final int) float64 {
		if start == 0 {
			return math.Inf(0)
		}
		return (float64(final - start)) / float64(start) * 100.0
	}, true)
}
var States = []string{StateActive, StateRequest, StateDeleted}
var Static embed.FS

Static contains all the static files to serve.

var Templates embed.FS

Templates contains all templates.

var Version = "dev"

Functions

func ChunkStat

func ChunkStat(stats []HitListStat) (int, []int)

Compress all the data in to 12 chunks.

func CopyContextValues

func CopyContextValues(ctx context.Context) context.Context

CopyContextValues creates a new context with the all the request values set.

Useful for tests, or for "removing" the timeout on the request context so it can be passed to background functions.

func DefaultLocale

func DefaultLocale() *z18n.Locale

func GetBundle

func GetBundle(ctx context.Context) *z18n.Bundle

func HorizontalChart

func HorizontalChart(ctx context.Context, stats HitStats, total int, link, paginate bool) template.HTML

func Import

func Import(
	ctx context.Context, fp io.Reader, replace, email bool,
	persist func(Hit, bool),
) (*time.Time, error)

Import data from an export.

The persist() callback will be called for every hit; you usually want to collect a bunch of them and then persist them.

After everything is done, this will be called once more with an empty hit and final set to true, to persist the last batch.

func InitGeoDB

func InitGeoDB(path string)

InitGeoDB sets up the geoDB database located at the given path.

The database can be the "Countries" or "Cities" version.

It will use the embeded "Countries" database if path is an empty string.

func ListCache

func ListCache(ctx context.Context) map[string]struct {
	Size  int64
	Items map[string]string
}

func LoadBufferKey

func LoadBufferKey(ctx context.Context) ([]byte, error)

func NewBufferKey

func NewBufferKey(ctx context.Context) (string, error)

func NewCache

func NewCache(ctx context.Context) context.Context

func NewConfig

func NewConfig(ctx context.Context) context.Context

func NewContext

func NewContext(db zdb.DB) context.Context

NewContext creates a new context with all values set.

func NewValidate

func NewValidate(ctx context.Context) zvalidate.Validator

func PathFilter

func PathFilter(ctx context.Context, filter string, matchTitle bool) ([]int64, error)

PathFilter returns a list of IDs matching the path name.

if matchTitle is true it will match the title as well.

func Translations

func Translations(ctx context.Context) fs.FS

Translations gets the translation messages; a user can have a local override, so we need to apply that per-user.

func UUID

func UUID() zint.Uint128

UUID created a new UUID v4.

func WithSite

func WithSite(ctx context.Context, s *Site) context.Context

WithSite adds the site to the context.

func WithUser

func WithUser(ctx context.Context, u *User) context.Context

WithUser adds the site to the context.

Types

type APIToken

type APIToken struct {
	ID     int64 `db:"api_token_id" json:"-"`
	SiteID int64 `db:"site_id" json:"-"`
	UserID int64 `db:"user_id" json:"-"`

	Name        string         `db:"name" json:"name"`
	Token       string         `db:"token" json:"-"`
	Permissions zint.Bitflag64 `db:"permissions" json:"permissions"`

	CreatedAt  time.Time  `db:"created_at" json:"-"`
	LastUsedAt *time.Time `db:"last_used_at" json:"-"`
}

func (*APIToken) ByID

func (t *APIToken) ByID(ctx context.Context, id int64) error

func (*APIToken) ByToken

func (t *APIToken) ByToken(ctx context.Context, token string) error

func (*APIToken) Defaults

func (t *APIToken) Defaults(ctx context.Context)

Defaults sets fields to default values, unless they're already set.

func (*APIToken) Delete

func (t *APIToken) Delete(ctx context.Context) error

func (APIToken) FormatPermissions

func (t APIToken) FormatPermissions() string

func (*APIToken) Insert

func (t *APIToken) Insert(ctx context.Context) error

Insert a new row.

func (APIToken) PermissionFlags

func (t APIToken) PermissionFlags(only ...zint.Bitflag64) []PermissionFlag

PermissionFlags returns a list of all flags we know for the Permissions settings.

func (*APIToken) Update

func (t *APIToken) Update(ctx context.Context) error

Update the name and permissions.

func (*APIToken) UpdateLastUsed

func (t *APIToken) UpdateLastUsed(ctx context.Context) error

UpdateLastUsed sets the last used time to the current time.

func (*APIToken) Validate

func (t *APIToken) Validate(ctx context.Context) error

type APITokens

type APITokens []APIToken

func (*APITokens) Delete

func (t *APITokens) Delete(ctx context.Context, _ bool) error

Delete all API tokens in this selection.

func (*APITokens) Find

func (t *APITokens) Find(ctx context.Context, ident []string) error

Find API tokens: by ID if ident is a number, or by token if it's not.

func (*APITokens) IDs

func (t *APITokens) IDs() []int64

IDs gets a list of all IDs for these API tokens.

func (*APITokens) List

func (t *APITokens) List(ctx context.Context) error

type BosmangStat

type BosmangStat struct {
	ID        int64     `db:"site_id"`
	Codes     string    `db:"codes"`
	Email     string    `db:"email"`
	CreatedAt time.Time `db:"created_at"`
	LastMonth int       `db:"last_month"`
	Total     int       `db:"total"`
	Avg       int       `db:"avg"`
}

type BosmangStats

type BosmangStats []BosmangStat

func (*BosmangStats) List

func (a *BosmangStats) List(ctx context.Context) error

List stats for all sites, for all time.

type Browser

type Browser struct {
	ID      int64  `db:"browser_id"`
	Name    string `db:"name"`
	Version string `db:"version"`
}

func (*Browser) GetOrInsert

func (b *Browser) GetOrInsert(ctx context.Context, name, version string) error

type Campaign

type Campaign struct {
	ID     int64  `db:"campaign_id" json:"campaign_id"`
	SiteID int64  `db:"site_id" json:"site_id"`
	Name   string `db:"name" json:"name"`
}

func (*Campaign) ByName

func (c *Campaign) ByName(ctx context.Context, name string) error

func (*Campaign) Defaults

func (c *Campaign) Defaults(ctx context.Context)

func (*Campaign) Insert

func (c *Campaign) Insert(ctx context.Context) error

func (*Campaign) Validate

func (c *Campaign) Validate() error

type CollectFlag

type CollectFlag struct {
	Label, Help string
	Flag        zint.Bitflag16
}

type Export

type Export struct {
	ID     int64 `db:"export_id" json:"id,readonly"`
	SiteID int64 `db:"site_id" json:"site_id,readonly"`

	// The hit ID this export was started from.
	StartFromHitID int64 `db:"start_from_hit_id" json:"start_from_hit_id"`

	// Last hit ID that was exported; can be used as start_from_hit_id.
	LastHitID *int64 `db:"last_hit_id" json:"last_hit_id,readonly"`

	Path      string    `db:"path" json:"path,readonly"` // {omitdoc}
	CreatedAt time.Time `db:"created_at" json:"created_at,readonly"`

	FinishedAt *time.Time `db:"finished_at" json:"finished_at,readonly"`
	NumRows    *int       `db:"num_rows" json:"num_rows,readonly"`

	// File size in MB.
	Size *string `db:"size" json:"size,readonly"`

	// SHA256 hash.
	Hash *string `db:"hash" json:"hash,readonly"`

	// Any errors that may have occured.
	Error *string `db:"error" json:"error,readonly"`
}

func (*Export) ByID

func (e *Export) ByID(ctx context.Context, id int64) error

func (*Export) Create

func (e *Export) Create(ctx context.Context, startFrom int64) (*os.File, error)

Create a new export.

Inserts a row in exports table and returns open file pointer to the destination file.

func (Export) Exists

func (e Export) Exists() bool

func (*Export) Run

func (e *Export) Run(ctx context.Context, fp *os.File, mailUser bool)

Export all data to a CSV file.

type ExportRow

type ExportRow struct {
	ID     int64 `db:"hit_id"`
	SiteID int64 `db:"site_id"`

	Path  string `db:"path"`
	Title string `db:"title"`
	Event string `db:"event"`

	UserAgent string `db:"ua"`
	Browser   string `db:"browser"`
	System    string `db:"system"`

	Session    zint.Uint128 `db:"session"`
	Bot        string       `db:"bot"`
	Ref        string       `db:"ref"`
	RefScheme  string       `db:"ref_s"`
	Size       string       `db:"size"`
	Location   string       `db:"loc"`
	FirstVisit string       `db:"first"`
	CreatedAt  string       `db:"created_at"`
}

func (ExportRow) Hit

func (row ExportRow) Hit(ctx context.Context, siteID int64) (Hit, error)

func (*ExportRow) Read

func (row *ExportRow) Read(line []string) error

type ExportRows

type ExportRows []ExportRow

func (*ExportRows) Export

func (h *ExportRows) Export(ctx context.Context, limit, paginate int64) (int64, error)

Export all hits for a site, including bot requests.

type Exports

type Exports []Export

func (*Exports) List

func (e *Exports) List(ctx context.Context) error

type Floats

type Floats []float64

Floats stores a slice of []float64 as a comma-separated string.

func (Floats) MarshalText

func (l Floats) MarshalText() ([]byte, error)

func (*Floats) Scan

func (l *Floats) Scan(v any) error

func (Floats) String

func (l Floats) String() string

func (*Floats) UnmarshalText

func (l *Floats) UnmarshalText(v []byte) error

func (Floats) Value

func (l Floats) Value() (driver.Value, error)

type GlobalConfig

type GlobalConfig struct {
	Domain         string
	DomainStatic   string
	DomainCount    string
	URLStatic      string
	Dev            bool
	GoatcounterCom bool
	Port           string
	Websocket      bool
	EmailFrom      string
	BcryptMinCost  bool
}

func Config

func Config(ctx context.Context) *GlobalConfig

type Hit

type Hit struct {
	ID         int64        `db:"hit_id" json:"-"`
	Site       int64        `db:"site_id" json:"-"`
	PathID     int64        `db:"path_id" json:"-"`
	RefID      int64        `db:"ref_id" json:"-"`
	SizeID     *int64       `db:"size_id" json:"-"`
	BrowserID  int64        `db:"browser_id" json:"-"`
	SystemID   int64        `db:"system_id" json:"-"`
	CampaignID *int64       `db:"campaign" json:"-"`
	Session    zint.Uint128 `db:"session" json:"-"`

	Path  string     `db:"-" json:"p,omitempty"`
	Title string     `db:"-" json:"t,omitempty"`
	Ref   string     `db:"-" json:"r,omitempty"`
	Event zbool.Bool `db:"-" json:"e,omitempty"`
	Size  Floats     `db:"-" json:"s,omitempty"`
	Query string     `db:"-" json:"q,omitempty"`
	Bot   int        `db:"bot" json:"b,omitempty"`

	RefScheme       *string    `db:"ref_scheme" json:"-"`
	UserAgentHeader string     `db:"-" json:"-"`
	Location        string     `db:"location" json:"-"`
	Language        *string    `db:"language" json:"-"`
	FirstVisit      zbool.Bool `db:"first_visit" json:"-"`
	CreatedAt       time.Time  `db:"created_at" json:"-"`

	RefURL *url.URL `db:"-" json:"-"`   // Parsed Ref
	Random string   `db:"-" json:"rnd"` // Browser cache buster, as they don't always listen to Cache-Control

	// Some values we need to pass from the HTTP handler to memstore
	RemoteAddr    string `db:"-" json:"-"`
	UserSessionID string `db:"-" json:"-"`
	// contains filtered or unexported fields
}

func (*Hit) Defaults

func (h *Hit) Defaults(ctx context.Context, initial bool) error

Defaults sets fields to default values, unless they're already set.

func (*Hit) Ignore

func (h *Hit) Ignore() bool

func (*Hit) Validate

func (h *Hit) Validate(ctx context.Context, initial bool) error

Validate the object.

type HitList

type HitList struct {
	// Number of visitors for the selected date range.
	Count int `db:"count" json:"count"`

	// Path ID
	PathID int64 `db:"path_id" json:"path_id"`

	// Path name (e.g. /hello.html).
	Path string `db:"path" json:"path"`

	// Is this an event?
	Event zbool.Bool `db:"event" json:"event"`

	// Page title.
	Title string `db:"title" json:"title"`

	// Highest visitors per hour or day (depending on daily being set).
	Max int `json:"max"`

	// Statistics by day and hour.
	Stats []HitListStat `json:"stats"`

	// What kind of referral this is; only set when retrieving referrals {enum: h g c o}.
	//
	//  h   HTTP Referal header.
	//  g   Generated; for example are Google domains (google.com, google.nl,
	//      google.co.nz, etc.) are grouped as the generated referral "Google".
	//  c   Campaign (via query parameter)
	//  o   Other
	RefScheme *string `db:"ref_scheme" json:"ref_scheme,omitempty"`
}

func (*HitList) PathCount

func (h *HitList) PathCount(ctx context.Context, path string, rng ztime.Range) error

PathCount gets the visit count for one path.

func (*HitList) SiteTotalUTC

func (h *HitList) SiteTotalUTC(ctx context.Context, rng ztime.Range) error

SiteTotal gets the total counts for all paths. This always uses UTC.

func (*HitList) Totals

func (h *HitList) Totals(ctx context.Context, rng ztime.Range, pathFilter []int64, daily, noEvents bool) (int, error)

Totals gets the data for the "Totals" chart/widget.

type HitListStat

type HitListStat struct {
	Day    string `json:"day"`    // Day these statistics are for {date}.
	Hourly []int  `json:"hourly"` // Visitors per hour.
	Daily  int    `json:"daily"`  // Total visitors for this day.
}

type HitLists

type HitLists []HitList

func (HitLists) Diff

func (h HitLists) Diff(ctx context.Context, rng, prev ztime.Range) ([]float64, error)

Diff gets the difference in percentage of all paths in this HitList.

e.g. if called with start=2020-01-20; end=2020-01-2020-01-27, then it will compare this to start=2020-01-12; end=2020-01-19

The return value is in the same order as paths.

func (*HitLists) List

func (h *HitLists) List(
	ctx context.Context, rng ztime.Range, pathFilter, exclude []int64, limit int, daily bool,
) (int, bool, error)

List the top paths for this site in the given time period.

func (*HitLists) ListPathsLike

func (h *HitLists) ListPathsLike(ctx context.Context, search string, matchTitle, matchCase bool) error

ListPathsLike lists all paths matching the like pattern.

type HitStat

type HitStat struct {
	// ID for selecting more details; not present in the detail view.
	ID    string `db:"id" json:"id,omitempty"`
	Name  string `db:"name" json:"name"`   // Display name.
	Count int    `db:"count" json:"count"` // Number of visitors.

	// What kind of referral this is; only set when retrieving referrals {enum: h g c o}.
	//
	//  h   HTTP Referal header.
	//  g   Generated; for example are Google domains (google.com, google.nl,
	//      google.co.nz, etc.) are grouped as the generated referral "Google".
	//  c   Campaign (via query parameter)
	//  o   Other
	RefScheme *string `db:"ref_scheme" json:"ref_scheme,omitempty"`
}

type HitStats

type HitStats struct {
	More  bool      `json:"more"`
	Stats []HitStat `json:"stats"`
}

func (*HitStats) ListBrowser

func (h *HitStats) ListBrowser(ctx context.Context, browser string, rng ztime.Range, pathFilter []int64, limit, offset int) error

ListBrowser lists all the versions for one browser.

func (*HitStats) ListBrowsers

func (h *HitStats) ListBrowsers(ctx context.Context, rng ztime.Range, pathFilter []int64, limit, offset int) error

ListBrowsers lists all browser statistics for the given time period.

func (*HitStats) ListCampaign

func (h *HitStats) ListCampaign(ctx context.Context, campaign int64, rng ztime.Range, pathFilter []int64, limit, offset int) error

ListCampaign lists all statistics for a campaign.

func (*HitStats) ListCampaigns

func (h *HitStats) ListCampaigns(ctx context.Context, rng ztime.Range, pathFilter []int64, limit, offset int) error

ListCampaigns lists all campaigns statistics for the given time period.

func (*HitStats) ListLanguages

func (h *HitStats) ListLanguages(ctx context.Context, rng ztime.Range, pathFilter []int64, limit, offset int) error

ListLanguages lists all language statistics for the given time period.

func (*HitStats) ListLocation

func (h *HitStats) ListLocation(ctx context.Context, country string, rng ztime.Range, pathFilter []int64, limit, offset int) error

ListLocation lists all divisions for a location

func (*HitStats) ListLocations

func (h *HitStats) ListLocations(ctx context.Context, rng ztime.Range, pathFilter []int64, limit, offset int) error

ListLocations lists all location statistics for the given time period.

func (*HitStats) ListRefsByPathID

func (h *HitStats) ListRefsByPathID(ctx context.Context, pathID int64, rng ztime.Range, limit, offset int) error

ListRefsByPath lists all references for a pathID.

func (*HitStats) ListSize

func (h *HitStats) ListSize(ctx context.Context, id string, rng ztime.Range, pathFilter []int64, limit, offset int) error

ListSize lists all sizes for one grouping.

func (*HitStats) ListSizes

func (h *HitStats) ListSizes(ctx context.Context, rng ztime.Range, pathFilter []int64) error

ListSizes lists all device sizes.

func (*HitStats) ListSystem

func (h *HitStats) ListSystem(ctx context.Context, system string, rng ztime.Range, pathFilter []int64, limit, offset int) error

ListSystem lists all the versions for one system.

func (*HitStats) ListSystems

func (h *HitStats) ListSystems(ctx context.Context, rng ztime.Range, pathFilter []int64, limit, offset int) error

ListSystems lists OS statistics for the given time period.

func (*HitStats) ListTopRef

func (h *HitStats) ListTopRef(ctx context.Context, ref string, rng ztime.Range, pathFilter []int64, limit, offset int) error

ListTopRef lists all paths by referrer.

func (*HitStats) ListTopRefs

func (h *HitStats) ListTopRefs(ctx context.Context, rng ztime.Range, pathFilter []int64, limit, offset int) error

ListTopRefs lists all ref statistics for the given time period, excluding referrals from the configured LinkDomain.

The returned count is the count without LinkDomain, and is different from the total number of hits.

type Hits

type Hits []Hit

func (*Hits) Merge

func (h *Hits) Merge(ctx context.Context, dst int64, pathIDs []int64) error

Merge the given paths.

func (*Hits) Purge

func (h *Hits) Purge(ctx context.Context, pathIDs []int64) error

Purge the given paths.

func (*Hits) TestList

func (h *Hits) TestList(ctx context.Context, siteOnly bool) error

TestList lists all hits, for all sites, with browser_id, system_id, and paths set.

This is intended for tests.

type Ints

type Ints []int64

Ints stores a slice of []int64 as a comma-separated string.

func (Ints) MarshalText

func (l Ints) MarshalText() ([]byte, error)

func (*Ints) Scan

func (l *Ints) Scan(v any) error

func (Ints) String

func (l Ints) String() string

func (*Ints) UnmarshalText

func (l *Ints) UnmarshalText(v []byte) error

func (Ints) Value

func (l Ints) Value() (driver.Value, error)

type Location

type Location struct {
	ID int64 `db:"location_id"`

	Country     string `db:"country"`
	Region      string `db:"region"`
	CountryName string `db:"country_name"`
	RegionName  string `db:"region_name"`

	// TODO: send patch to staticcheck to deal with this better. This shouldn't
	// errror since "ISO" is an initialism.
	ISO3166_2 string `db:"iso_3166_2"` //lint:ignore ST1003 staticcheck bug
}

func (*Location) ByCode

func (l *Location) ByCode(ctx context.Context, code string) error

ByCode gets a location by ISO-3166-2 code; e.g. "US" or "US-TX".

func (*Location) Lookup

func (l *Location) Lookup(ctx context.Context, ip string) error

Lookup a location by IPv4 or IPv6 address.

This will insert a row in the locations table if one doesn't exist yet.

func (Location) LookupIP

func (l Location) LookupIP(ctx context.Context, ip string) string

LookupIP is a shorthand for Lookup(); returns id 1 on errors ("unknown").

type Locations

type Locations []Location

func (*Locations) ListCountries

func (l *Locations) ListCountries(ctx context.Context) error

ListCountries lists all counties. The region code/name will always be blank.

type OverrideTranslation

type OverrideTranslation struct {
	Name    string       `json:"name"`
	Updated string       `json:"updated"`
	File    msgfile.File `json:"file"`
}

type OverrideTranslations

type OverrideTranslations []OverrideTranslation

func (*OverrideTranslations) Get

func (o *OverrideTranslations) Get(ctx context.Context, insert bool) error

func (*OverrideTranslations) Insert

func (o *OverrideTranslations) Insert(ctx context.Context) error

Insert new.

func (OverrideTranslations) Key

func (*OverrideTranslations) Update

func (o *OverrideTranslations) Update(ctx context.Context) error

type Path

type Path struct {
	ID    int64      `db:"path_id" json:"id"` // Path ID
	Site  int64      `db:"site_id" json:"-"`
	Path  string     `db:"path" json:"path"`   // Path name
	Title string     `db:"title" json:"title"` // Page title
	Event zbool.Bool `db:"event" json:"event"` // Is this an event?
}

func (*Path) ByID

func (p *Path) ByID(ctx context.Context, id int64) error

func (*Path) Defaults

func (p *Path) Defaults(ctx context.Context)

func (*Path) GetOrInsert

func (p *Path) GetOrInsert(ctx context.Context) error

func (*Path) Validate

func (p *Path) Validate(ctx context.Context) error

type Paths

type Paths []Path

func (*Paths) List

func (p *Paths) List(ctx context.Context, siteID, after int64, limit int) (bool, error)

List all paths for a site.

type PermissionFlag

type PermissionFlag struct {
	Label, Help string
	Flag        zint.Bitflag64
}

type Ref

type Ref struct {
	ID        int64   `db:"ref_id"`
	Ref       string  `db:"ref"`
	RefScheme *string `db:"ref_scheme"`
}

func (*Ref) Defaults

func (r *Ref) Defaults(ctx context.Context)

func (*Ref) GetOrInsert

func (r *Ref) GetOrInsert(ctx context.Context) error

func (*Ref) Validate

func (r *Ref) Validate(ctx context.Context) error

type Site

type Site struct {
	ID     int64  `db:"site_id" json:"id,readonly"`
	Parent *int64 `db:"parent" json:"parent,readonly"`

	// Custom domain, e.g. "stats.example.com".
	//
	// When self-hosting this is the domain/vhost your site is accessible at.
	Cname *string `db:"cname" json:"cname"`

	// When the CNAME was verified.
	CnameSetupAt *time.Time `db:"cname_setup_at" json:"cname_setup_at,readonly"`

	// Domain code (e.g. "arp242", which makes arp242.goatcounter.com). Only
	// used for goatcounter.com and not when self-hosting.
	Code string `db:"code" json:"code"`

	// Site domain for linking (www.arp242.net).
	LinkDomain string `db:"link_domain" json:"link_domain"`

	Settings     SiteSettings `db:"settings" json:"setttings"`
	UserDefaults UserSettings `db:"user_defaults" json:"user_defaults"`

	// Whether this site has received any data; will be true after the first
	// pageview.
	ReceivedData bool `db:"received_data" json:"received_data"`

	// {omitdoc}
	Notes string `db:"notes" json:"-"`

	State      string     `db:"state" json:"state"`
	CreatedAt  time.Time  `db:"created_at" json:"created_at"`
	UpdatedAt  *time.Time `db:"updated_at" json:"updated_at"`
	FirstHitAt time.Time  `db:"first_hit_at" json:"first_hit_at"`
}

func GetAccount

func GetAccount(ctx context.Context) (*Site, error)

GetAccount gets this site's "account site" on which the users, etc. are stored.

func GetSite

func GetSite(ctx context.Context) *Site

GetSite gets the current site.

func MustGetAccount

func MustGetAccount(ctx context.Context) *Site

func MustGetSite

func MustGetSite(ctx context.Context) *Site

MustGetSite behaves as GetSite(), panicking if this fails.

func (*Site) ByCode

func (s *Site) ByCode(ctx context.Context, code string) error

ByCode gets a site by code.

func (*Site) ByHost

func (s *Site) ByHost(ctx context.Context, host string) error

ByHost gets a site by host name.

func (*Site) ByID

func (s *Site) ByID(ctx context.Context, id int64) error

ByID gets a site by ID.

func (*Site) ByIDState

func (s *Site) ByIDState(ctx context.Context, id int64, state string) error

ByIDState gets a site by ID and state. This may return deleted sites.

func (Site) ClearCache

func (s Site) ClearCache(ctx context.Context, full bool)

ClearCache clears the cache for this site.

func (*Site) Defaults

func (s *Site) Defaults(ctx context.Context)

Defaults sets fields to default values, unless they're already set.

func (*Site) Delete

func (s *Site) Delete(ctx context.Context, deleteChildren bool) error

Delete a site and all child sites.

func (Site) DeleteAll

func (s Site) DeleteAll(ctx context.Context) error

DeleteAll deletes all pageviews for this site, keeping the site itself and user intact.

func (Site) DeleteOlderThan

func (s Site) DeleteOlderThan(ctx context.Context, days int) error

func (Site) Display

func (s Site) Display(ctx context.Context) string

Display format: just the domain (cname or code+domain).

func (Site) Domain

func (s Site) Domain(ctx context.Context) string

Domain gets the global default domain, or this site's configured custom domain.

func (Site) Exists

func (s Site) Exists(ctx context.Context) (int64, error)

Exists checks if this site already exists, based on either the Cname or Code field.

func (*Site) Find

func (s *Site) Find(ctx context.Context, ident string) error

Find a site: by ID if ident is a number, or by host if it's not.

func (Site) IDOrParent

func (s Site) IDOrParent() int64

IDOrParent gets this site's ID or the parent ID if that's set.

func (*Site) Insert

func (s *Site) Insert(ctx context.Context) error

Insert a new row.

func (Site) LinkDomainURL

func (s Site) LinkDomainURL(withProto bool, paths ...string) string

LinkDomainURL creates a valid url to the configured LinkDomain.

func (*Site) ListSubs

func (s *Site) ListSubs(ctx context.Context) ([]string, error)

ListSubs lists all subsites, including the current site and parent.

func (Site) URL

func (s Site) URL(ctx context.Context) string

URL to this site.

func (Site) Undelete

func (s Site) Undelete(ctx context.Context, id int64) error

func (*Site) Update

func (s *Site) Update(ctx context.Context) error

Update existing site. Sets settings, cname, link_domain.

func (*Site) UpdateCnameSetupAt

func (s *Site) UpdateCnameSetupAt(ctx context.Context) error

UpdateCnameSetupAt confirms the custom domain was setup correct.

func (*Site) UpdateCode

func (s *Site) UpdateCode(ctx context.Context, code string) error

UpdateCode changes the site's domain code (e.g. "test" in "test.goatcounter.com").

func (*Site) UpdateFirstHitAt

func (s *Site) UpdateFirstHitAt(ctx context.Context, f time.Time) error

func (*Site) UpdateParent

func (s *Site) UpdateParent(ctx context.Context, newParent *int64) error

func (*Site) UpdateReceivedData

func (s *Site) UpdateReceivedData(ctx context.Context) error

func (*Site) Validate

func (s *Site) Validate(ctx context.Context) error

Validate the object.

type SiteSettings

type SiteSettings struct {
	Public         string         `json:"public"`
	Secret         string         `json:"secret"`
	AllowCounter   bool           `json:"allow_counter"`
	AllowBosmang   bool           `json:"allow_bosmang"`
	DataRetention  int            `json:"data_retention"`
	Campaigns      Strings        `json:"-"`
	IgnoreIPs      Strings        `json:"ignore_ips"`
	Collect        zint.Bitflag16 `json:"collect"`
	CollectRegions Strings        `json:"collect_regions"`
	AllowEmbed     Strings        `json:"allow_embed"`
}

SiteSettings contains all the user-configurable settings for a site, with the exception of the domain settings.

This is stored as JSON in the database.

func (SiteSettings) CanView

func (ss SiteSettings) CanView(token string) bool

func (SiteSettings) CollectFlags

func (ss SiteSettings) CollectFlags(ctx context.Context) []CollectFlag

CollectFlags returns a list of all flags we know for the Collect settings.

func (*SiteSettings) Defaults

func (ss *SiteSettings) Defaults(ctx context.Context)

func (SiteSettings) IsPublic

func (ss SiteSettings) IsPublic() bool

func (*SiteSettings) Scan

func (ss *SiteSettings) Scan(v any) error

func (SiteSettings) String

func (ss SiteSettings) String() string

func (*SiteSettings) Validate

func (ss *SiteSettings) Validate(ctx context.Context) error

func (SiteSettings) Value

func (ss SiteSettings) Value() (driver.Value, error)

type Sites

type Sites []Site

Sites is a list of sites.

func (*Sites) ContainsCNAME

func (s *Sites) ContainsCNAME(ctx context.Context, cname string) (bool, error)

ContainsCNAME reports if there is a site with this CNAME set.

func (*Sites) Delete

func (s *Sites) Delete(ctx context.Context, deleteChildren bool) error

Delete all sites in this selection.

func (*Sites) Find

func (s *Sites) Find(ctx context.Context, ident []string) error

Find sites: by ID if ident is a number, or by host if it's not.

func (*Sites) ForThisAccount

func (s *Sites) ForThisAccount(ctx context.Context, excludeCurrent bool) error

ForThisAccount gets all sites associated with this account.

func (*Sites) IDs

func (s *Sites) IDs() []int64

IDs gets a list of all IDs for these sites.

func (*Sites) ListIDs

func (s *Sites) ListIDs(ctx context.Context, ids ...int64) error

ListIDs lists all sites with the given IDs.

func (*Sites) ListSubs

func (s *Sites) ListSubs(ctx context.Context) error

ListSubs lists all subsites for the current site.

func (*Sites) OldSoftDeleted

func (s *Sites) OldSoftDeleted(ctx context.Context) error

OldSoftDeleted finds all sites which have been soft-deleted more than a week ago.

func (*Sites) UnscopedList

func (s *Sites) UnscopedList(ctx context.Context) error

UnscopedList lists all sites, not scoped to the current user.

func (*Sites) UnscopedListCnames

func (s *Sites) UnscopedListCnames(ctx context.Context) error

UnscopedListCnames all sites that have CNAME set, not scoped to the current user.

type Size

type Size struct {
	ID     int64   `db:"size_id"`
	Width  int16   `db:"width"`
	Height int16   `db:"height"`
	Scale  float64 `db:"scale"`
	Size   string  `db:"size"`
}

func (*Size) Defaults

func (s *Size) Defaults(ctx context.Context)

func (*Size) GetOrInsert

func (s *Size) GetOrInsert(ctx context.Context, size Floats) error

func (Size) String

func (s Size) String() string

func (*Size) Validate

func (s *Size) Validate(ctx context.Context) error

type Strings

type Strings []string

Strings stores a slice of []string as a comma-separated string.

func (Strings) MarshalText

func (l Strings) MarshalText() ([]byte, error)

func (*Strings) Scan

func (l *Strings) Scan(v any) error

func (Strings) String

func (l Strings) String() string

func (*Strings) UnmarshalText

func (l *Strings) UnmarshalText(v []byte) error

func (Strings) Value

func (l Strings) Value() (driver.Value, error)

type System

type System struct {
	ID      int64  `db:"system_id"`
	Name    string `db:"name"`
	Version string `db:"version"`
}

func (*System) GetOrInsert

func (s *System) GetOrInsert(ctx context.Context, name, version string) error

type TotalCount

type TotalCount struct {
	Total       int `db:"total" json:"total"`               // Total number of visitors (including events).
	TotalEvents int `db:"total_events" json:"total_events"` // Total number of visitors for events.
	// Total number of visitors in UTC. The browser, system, etc, stats are
	// always in UTC.
	TotalUTC int `db:"total_utc" json:"total_utc"`
}

func GetTotalCount

func GetTotalCount(ctx context.Context, rng ztime.Range, pathFilter []int64, noEvents bool) (TotalCount, error)

GetTotalCount gets the total number of pageviews for the selected timeview in the timezone the user configured.

This also gets the total number of pageviews for the selected time period in UTC. This is needed since the _stats tables are per day, rather than per-hour, so we need to use the correct totals to make sure the percentage calculations are accurate.

type TplEmailAddUser

type TplEmailAddUser struct {
	Context context.Context
	Site    Site
	NewUser User
	AddedBy string
}

func (TplEmailAddUser) Render

func (t TplEmailAddUser) Render() ([]byte, error)

type TplEmailExportDone

type TplEmailExportDone struct {
	Context context.Context
	Site    Site
	User    User
	Export  Export
}

func (TplEmailExportDone) Render

func (t TplEmailExportDone) Render() ([]byte, error)

type TplEmailForgotSite

type TplEmailForgotSite struct {
	Context context.Context
	Sites   Sites
	Email   string
}

func (TplEmailForgotSite) Render

func (t TplEmailForgotSite) Render() ([]byte, error)

type TplEmailImportDone

type TplEmailImportDone struct {
	Context context.Context
	Site    Site
	Rows    int
	Errors  *errors.Group
}

func (TplEmailImportDone) Render

func (t TplEmailImportDone) Render() ([]byte, error)

type TplEmailImportError

type TplEmailImportError struct {
	Context context.Context
	Error   error
}

func (TplEmailImportError) Render

func (t TplEmailImportError) Render() ([]byte, error)

type TplEmailPasswordReset

type TplEmailPasswordReset struct {
	Context context.Context
	Site    Site
	User    User
}

func (TplEmailPasswordReset) Render

func (t TplEmailPasswordReset) Render() ([]byte, error)

type TplEmailVerify

type TplEmailVerify struct {
	Context context.Context
	Site    Site
	User    User
}

func (TplEmailVerify) Render

func (t TplEmailVerify) Render() ([]byte, error)

type TplEmailWelcome

type TplEmailWelcome struct {
	Context     context.Context
	Site        Site
	User        User
	CountDomain string
}

func (TplEmailWelcome) Render

func (t TplEmailWelcome) Render() ([]byte, error)

type Update

type Update struct {
	ID        int64     `db:"id"`
	Subject   string    `db:"subject"`
	Body      string    `db:"body"`
	CreatedAt time.Time `db:"created_at"`
	ShowAt    time.Time `db:"show_at"`
	Seen      bool      `db:"-"`
}

type Updates

type Updates []Update

func (*Updates) HasSince

func (u *Updates) HasSince(ctx context.Context, since time.Time) (bool, error)

HasSince reports if there are any updates since the given date.

func (*Updates) List

func (u *Updates) List(ctx context.Context, since time.Time) error

List all updates.

type User

type User struct {
	ID   int64 `db:"user_id" json:"id,readonly"`
	Site int64 `db:"site_id" json:"site,readonly"`

	Email         string       `db:"email" json:"email"`
	EmailVerified zbool.Bool   `db:"email_verified" json:"email_verified,readonly"`
	Password      []byte       `db:"password" json:"-"`
	TOTPEnabled   zbool.Bool   `db:"totp_enabled" json:"totp_enabled,readonly"`
	TOTPSecret    []byte       `db:"totp_secret" json:"-"`
	Access        UserAccesses `db:"access" json:"access,readonly"`
	LoginAt       *time.Time   `db:"login_at" json:"login_at,readonly"`
	OpenAt        *time.Time   `db:"open_at" json:"open_at,readonly"`
	ResetAt       *time.Time   `db:"reset_at" json:"reset_at,readonly"`
	LoginRequest  *string      `db:"login_request" json:"-"`
	LoginToken    *string      `db:"login_token" json:"-"`
	Token         *string      `db:"csrf_token" json:"-"`
	EmailToken    *string      `db:"email_token" json:"-"`
	SeenUpdatesAt time.Time    `db:"seen_updates_at" json:"-"`
	Settings      UserSettings `db:"settings" json:"settings"`

	// Keep track when the last email report was sent, so we don't double-send them.
	LastReportAt time.Time `db:"last_report_at" json:"last_report_at"`

	CreatedAt time.Time  `db:"created_at" json:"created_at,readonly"`
	UpdatedAt *time.Time `db:"updated_at" json:"updated_at,readonly"`
}

User entry.

func GetUser

func GetUser(ctx context.Context) *User

GetUser gets the currently logged in user.

func MustGetUser

func MustGetUser(ctx context.Context) *User

MustGetUser behaves as GetUser(), panicking if this fails.

func (User) AccessAdmin

func (u User) AccessAdmin() bool

func (User) AccessSettings

func (u User) AccessSettings() bool

func (User) AccessSuperuser

func (u User) AccessSuperuser() bool

func (*User) ByEmail

func (u *User) ByEmail(ctx context.Context, email string) error

ByEmail gets a user by email address.

func (*User) ByEmailToken

func (u *User) ByEmailToken(ctx context.Context, key string) error

ByEmailToken gets a user by email verification token.

func (*User) ByID

func (u *User) ByID(ctx context.Context, id int64) error

ByID gets a user by id.

func (*User) ByResetToken

func (u *User) ByResetToken(ctx context.Context, key string) error

ByResetToken gets a user by login request key.

This can be used in two contexts: the user requested a password reset, or the user was invited to create a new account.

func (*User) ByToken

func (u *User) ByToken(ctx context.Context, token string) error

ByToken gets a user by login token.

func (*User) ByTokenAndSite

func (u *User) ByTokenAndSite(ctx context.Context, token string) error

ByTokenAndSite gets a user by login token.

func (*User) CSRFToken

func (u *User) CSRFToken() string

CSRFToken gets the CSRF token.

func (User) CorrectPassword

func (u User) CorrectPassword(pwd string) (bool, error)

CorrectPassword verifies that this password is correct.

func (*User) Defaults

func (u *User) Defaults(ctx context.Context)

Defaults sets fields to default values, unless they're already set.

func (*User) Delete

func (u *User) Delete(ctx context.Context, lastAdmin bool) error

Delete this user.

func (*User) DisableTOTP

func (u *User) DisableTOTP(ctx context.Context) error

func (User) EmailReportRange

func (u User) EmailReportRange() ztime.Range

EmailReportRange gets the time range of the next report to send out.

user.LastReportAt is set when a report is sent; to get the range for the new report we take LastReportAt, go to the start and end of the period, and if the endDate > now then send out a new report and set LastReportAt.

The cronjob will send the report if the current date is after the end date.

func (User) EmailShort

func (u User) EmailShort() string

func (*User) EnableTOTP

func (u *User) EnableTOTP(ctx context.Context) error

func (*User) Find

func (u *User) Find(ctx context.Context, ident string) error

Find a user: by ID if ident is a number, or by email if it's not.

func (User) HasAccess

func (u User) HasAccess(check UserAccess) bool

HasAccess checks if this user has access to this site for the permission.

func (*User) Insert

func (u *User) Insert(ctx context.Context, allowBlankPassword bool) error

Insert a new row.

func (*User) InviteToken

func (u *User) InviteToken(ctx context.Context) error

func (*User) Login

func (u *User) Login(ctx context.Context) error

Login a user; create a new key, CSRF token, and reset the request date.

func (*User) Logout

func (u *User) Logout(ctx context.Context) error

Logout a user.

func (*User) RequestReset

func (u *User) RequestReset(ctx context.Context) error

RequestReset generates a new password reset key.

func (*User) SeenUpdates

func (u *User) SeenUpdates(ctx context.Context) error

SeenUpdates marks this user as having seen all updates up until now.

func (*User) Update

func (u *User) Update(ctx context.Context, emailChanged bool) error

Update this user's name, email, settings, and access.

func (*User) UpdateOpenAt

func (u *User) UpdateOpenAt(ctx context.Context) error

func (*User) UpdatePassword

func (u *User) UpdatePassword(ctx context.Context, pwd string) error

UpdatePassword updates this user's password.

func (*User) UpdateSite

func (u *User) UpdateSite(ctx context.Context) error

UpdateSite updates this user's siteID (i.e. moves it to another site).

func (*User) Validate

func (u *User) Validate(ctx context.Context, validatePassword bool) error

Validate the object.

func (*User) VerifyEmail

func (u *User) VerifyEmail(ctx context.Context) error

type UserAccess

type UserAccess string
const (
	AccessReadOnly  UserAccess = "r"
	AccessSettings  UserAccess = "s"
	AccessAdmin     UserAccess = "a"
	AccessSuperuser UserAccess = "*"
)

func (UserAccess) String

func (u UserAccess) String() string

TODO: this is not translated.

type UserAccesses

type UserAccesses map[string]UserAccess

func (*UserAccesses) Scan

func (u *UserAccesses) Scan(v any) error

Scan converts the data returned from the DB into the struct.

func (UserAccesses) Value

func (u UserAccesses) Value() (driver.Value, error)

Value implements the SQL Value function to determine what to store in the DB.

type UserAgent

type UserAgent struct {
	UserAgent string
	Isbot     uint8
	BrowserID int64
	SystemID  int64
}

func (*UserAgent) GetOrInsert

func (p *UserAgent) GetOrInsert(ctx context.Context) error

type UserSettings

type UserSettings struct {
	TwentyFourHours       bool      `json:"twenty_four_hours"`
	SundayStartsWeek      bool      `json:"sunday_starts_week"`
	Language              string    `json:"language"`
	DateFormat            string    `json:"date_format"`
	NumberFormat          rune      `json:"number_format"`
	Timezone              *tz.Zone  `json:"timezone"`
	Widgets               Widgets   `json:"widgets"`
	Views                 Views     `json:"views"`
	EmailReports          zint.Int  `json:"email_reports"`
	FewerNumbers          bool      `json:"fewer_numbers"`
	FewerNumbersLockUntil time.Time `json:"fewer_numbers_lock_until"`
	Theme                 string    `json:"theme"`
}

UserSettings are all user preferences.

func (*UserSettings) Defaults

func (ss *UserSettings) Defaults(ctx context.Context)

func (*UserSettings) Scan

func (ss *UserSettings) Scan(v any) error

func (UserSettings) String

func (ss UserSettings) String() string

func (*UserSettings) Validate

func (ss *UserSettings) Validate(ctx context.Context) error

func (UserSettings) Value

func (ss UserSettings) Value() (driver.Value, error)

type Users

type Users []User

func (Users) Admins

func (u Users) Admins() Users

Admins returns just the admins and superusers in this user list.

func (*Users) ByEmail

func (u *Users) ByEmail(ctx context.Context, email string) error

ByEmail gets all users with this email address.

func (*Users) BySite

func (u *Users) BySite(ctx context.Context, siteID int64) error

BySite gets all users for a site.

func (*Users) Delete

func (u *Users) Delete(ctx context.Context, force bool) error

Delete all users in this selection.

func (*Users) Find

func (u *Users) Find(ctx context.Context, ident []string) error

Find users: by ID if ident is a number, or by email if it's not.

func (*Users) IDs

func (u *Users) IDs() []int64

IDs gets a list of all IDs for these users.

func (*Users) List

func (u *Users) List(ctx context.Context, siteID int64) error

List all users for a site.

type View

type View struct {
	Name   string `json:"name"`
	Filter string `json:"filter"`
	Daily  bool   `json:"daily"`
	Period string `json:"period"` // "week", "week-cur", or n days: "8"
}

type Views

type Views []View

Views for the dashboard; these settings apply to all widget and are configurable in the yellow box at the top.

func (Views) Get

func (v Views) Get(name string) (View, int)

Get a view for this site by name and returns the view and index. Returns -1 if this view doesn't exist.

type Widget

type Widget map[string]any

func NewWidget

func NewWidget(name string) Widget

func (Widget) GetSetting

func (w Widget) GetSetting(ctx context.Context, n string) any

func (Widget) GetSettings

func (w Widget) GetSettings(ctx context.Context) WidgetSettings

GetSettings gets all setting for this widget.

func (Widget) Name

func (w Widget) Name() string

Name gets this widget's name.

func (Widget) SetSetting

func (w Widget) SetSetting(ctx context.Context, widget, setting, value string) error

SetSettings set the setting "setting" for widget "widget" to "value".

The value is converted to the correct type for this setting.

type WidgetSetting

type WidgetSetting struct {
	Type        string
	Hidden      bool
	Label       string
	Help        string
	Options     [][2]string
	OptionsFunc func(context.Context) [][2]string
	Validate    func(*zvalidate.Validator, any)
	Value       any
}

type WidgetSettings

type WidgetSettings map[string]WidgetSetting

func (WidgetSettings) Display

func (s WidgetSettings) Display(ctx context.Context, wname string) string

Display all values that are different from the default.

func (WidgetSettings) HasSettings

func (s WidgetSettings) HasSettings() bool

HasSettings reports if there are any non-hidden settings.

func (*WidgetSettings) Set

func (s *WidgetSettings) Set(k string, v any)

type Widgets

type Widgets []Widget

Widgets is a list of widgets to be printed, in order.

func (Widgets) ByID

func (w Widgets) ByID(id int) Widget

ByID gets this widget by the position/ID.

func (Widgets) Get

func (w Widgets) Get(name string) Widgets

Get all widget from the list by name.

func (*Widgets) UnmarshalJSON

func (w *Widgets) UnmarshalJSON(d []byte) error

This exists as a work-around because a migration set this column wrong >_<

https://github.com/arp242/goatcounter/issues/569#issuecomment-1042013488

Source Files

api_token.go bosmang.go campaign.go context.go export.go helper.go helper_cgo.go hit.go hit_list.go hit_stats.go i18n.go locations.go memstore.go path.go ref.go refspam.go settings.go site.go size.go tpl.go types.go update.go user.go user_agent.go

Directories

PathSynopsis
acme
cmd
cmd/goatcounter
cronPackage cron schedules jobs.
db
db/migrate
db/migrate/gomig
gctestPackage gctest contains testing helpers.
handlers
logscan
metricsPackage metrics collects performance metrics.
widgets
Version
v2.5.0 (latest)
Published
Dec 14, 2023
Platform
linux/amd64
Imports
71 packages
Last checked
3 days ago

Tools for package owners.