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
)

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 (
	PlanTrial        = "trial"
	PlanFree         = "free"
	PlanPersonal     = "personal"
	PlanStarter      = "starter"
	PlanBusiness     = "business"
	PlanBusinessPlus = "businessplus"
	PlanChild        = "child"
)

Plan column values.

const (
	UserRoleNormal  = ""
	UserRoleBosmang = "bosmang"
)
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      = ptr("h")
	RefSchemeOther     = ptr("o")
	RefSchemeGenerated = ptr("g")
	RefSchemeCampaign  = ptr("c")
)

ref_scheme column

var DB embed.FS

DB contains all files in db/*

var GeoDB []byte

GeoDB contains the GeoIP countries database.

var Memstore ms
var PersistRunner = struct {
	Run chan struct{}
}{make(chan struct{})}

PersistRunner can be used to signal the cron package to run the PeristAndStat() function. We can't use a direct function call due to circular imports.

var PlanCodes = []string{PlanTrial, PlanFree, PlanPersonal, PlanStarter, PlanBusiness, PlanBusinessPlus}
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(float64((final-start)/start) * 100)
	}, 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 GetBundle

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

func GetMax

func GetMax(ctx context.Context, rng ztime.Range, pathFilter []int64, daily bool) (int, error)

GetMax gets the path with the higest number of pageviews per hour or day for this date range.

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 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:"-"`
}

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) 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 AccountUsage

type AccountUsage struct {
	Plan                          Plan
	ThisStart, PrevStart, PrevEnd time.Time
	Stats                         []AccountUsageStats
	Total                         AccountUsageStats
}

func (*AccountUsage) Get

func (a *AccountUsage) Get(ctx context.Context) error

type AccountUsageStats

type AccountUsageStats struct {
	Code       string `db:"code"`
	Total      int    `db:"total"`
	ThisPeriod int    `db:"this_period"`
	PrevPeriod int    `db:"prev_period"`
}

type BosmangSiteStat

type BosmangSiteStat struct {
	Account Site
	Sites   Sites
	Users   Users
	Usage   AccountUsage
}

func (*BosmangSiteStat) ByID

func (a *BosmangSiteStat) ByID(ctx context.Context, id int64) error

ByID gets stats for a single site.

func (*BosmangSiteStat) Find

func (a *BosmangSiteStat) Find(ctx context.Context, ident string) error

Find gets stats for a single site.

type BosmangStat

type BosmangStat struct {
	ID            int64     `db:"site_id"`
	Codes         string    `db:"codes"`
	Stripe        *string   `db:"stripe"`
	BillingAmount *string   `db:"billing_amount"`
	Email         string    `db:"email"`
	CreatedAt     time.Time `db:"created_at"`
	Plan          string    `db:"plan"`
	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 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 interface{}) 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
	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:"-"`
	UserAgentID *int64       `db:"user_agent_id" json:"-"`
	Session     zint.Uint128 `db:"session" json:"-"`

	Path  string     `db:"-" json:"p,omitempty"`
	Title string     `db:"-" json:"t,omitempty"`
	Ref   string     `db:"ref" json:"r,omitempty"`
	Event zbool.Bool `db:"-" json:"e,omitempty"`
	Size  Floats     `db:"size" 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:"-"`
	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:"-"`
	BrowserID     int64  `db:"-" json:"-"`
	SystemID      int64  `db:"-" json:"-"`
}

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 {
	Count       int        `db:"count"`
	CountUnique int        `db:"count_unique"`
	PathID      int64      `db:"path_id"`
	Path        string     `db:"path"`
	Event       zbool.Bool `db:"event"`
	Title       string     `db:"title"`
	RefScheme   *string    `db:"ref_scheme"`
	Max         int
	Stats       []HitListStat
}

func (*HitList) PathCount

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

PathCount gets the total and total_unique 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
	Hourly       []int
	HourlyUnique []int
	Daily        int
	DailyUnique  int
}

type HitLists

type HitLists []HitList

func (*HitLists) List

func (h *HitLists) List(
	ctx context.Context, rng ztime.Range, pathFilter, exclude []int64, limit int, daily bool,
) (int, 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 bool) error

ListPathsLike lists all paths matching the like pattern.

type HitStat

type HitStat struct {
	ID          string  `db:"id"`
	Name        string  `db:"name"`
	Count       int     `db:"count"`
	CountUnique int     `db:"count_unique"`
	RefScheme   *string `db:"ref_scheme"`
}

type HitStats

type HitStats struct {
	More  bool
	Stats []HitStat
}

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) 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) ListRefsByPath

func (h *HitStats) ListRefsByPath(ctx context.Context, path string, rng ztime.Range, limit, offset int) error

ListRefsByPath lists all references for a path.

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) Purge

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

Purge all 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 interface{}) 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"`
	Site int64 `db:"site_id"`

	Path  string     `db:"path"`
	Title string     `db:"title"`
	Event zbool.Bool `db:"event"`
}

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 PermissionFlag

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

type Plan

type Plan struct {
	Code        string
	Name        string
	Price       int
	MaxHits     int
	MonthlyHits int
}

Plan represents a plan people can subscribe to.

type Plans

type Plans []Plan

func (Plans) Find

func (p Plans) Find(s Site) Plan

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"`

	// Plan currently subscribed to.
	Plan string `db:"plan" json:"plan,readonly"`

	// Plan this site tried to subscribe to, but payment hasn't been verified
	// yet.
	PlanPending *string `db:"plan_pending" json:"plan_pending,readonly"`

	// Stripe customer ID.
	Stripe *string `db:"stripe" json:"stripe,readonly"`

	// When this plan is scheduled to be cancelled.
	PlanCancelAt *time.Time `db:"plan_cancel_at" json:"plan_cancel_at,readonly"`

	// Amount is being paid for the plan.
	BillingAmount *string `db:"billing_amount" json:"billing_amount,readonly"`

	// Maximum number of extra pageviews to charge for.
	ExtraPageviews *int `db:"extra_pageviews" json:"extra_pageviews,readonly"`

	// subscription_item in Stripe for extra pageviews (si_ ...)
	ExtraPageviewsSub *string `db:"extra_pageviews_sub" json:"-"`

	// "Anchor" for the billing period.
	//
	// This is the time someone subscribed to a plan; and their billing period
	// will start on this day.
	BillingAnchor *time.Time `db:"billing_anchor" json:"billing_anchor,readonly"`

	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, billing, 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) BillingAnchorDay

func (s Site) BillingAnchorDay() int

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) ByStripe

func (s *Site) ByStripe(ctx context.Context, stripe string) error

ByStripe gets a site by the Stripe customer ID.

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) NextInvoice

func (s Site) NextInvoice() time.Time

func (Site) PayExternal

func (s Site) PayExternal() string

PayExternal gets the external payment name, or an empty string if there is none.

func (Site) PlanCustomDomain

func (s Site) PlanCustomDomain(ctx context.Context) bool

PlanCustomDomain reports if this site's plan allows custom domains.

func (Site) PreviousBillingPeriod

func (s Site) PreviousBillingPeriod() ztime.Range

func (Site) ShowPayBanner

func (s Site) ShowPayBanner(ctx context.Context) bool

ShowPayBanner determines if we should show a "please pay" banner for the customer.

func (Site) StripeCustomer

func (s Site) StripeCustomer() bool

StripeCustomer reports if this Stripe column refers to a Stripe customer.

func (Site) Subscribed

func (s Site) Subscribed() bool

Subscribed reports if this customer is currently a paying customer subscribed to a plan. This may be payed outside of GoatCounter (e.g. GitHub).

func (Site) ThisBillingPeriod

func (s Site) ThisBillingPeriod() ztime.Range

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) UpdateStripe

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

UpdateStripe sets the billing info.

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:"campaigns"`
	IgnoreIPs      Strings        `json:"ignore_ips"`
	Collect        zint.Bitflag16 `json:"collect"`
	CollectRegions Strings        `json:"collect_regions"`
}

SiteSettings contains all the user-configurable settings for a site, with the exception of the domain and billing 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 interface{}) 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) ExpiredPlans

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

ExpiredPlans finds all sites which have a plan that's expired.

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) 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 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 interface{}) 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"`
	TotalUnique       int `db:"total_unique"`
	TotalUniqueUTC    int `db:"total_unique_utc"`
	TotalEvents       int `db:"total_events"`
	TotalEventsUnique int `db:"total_events_unique"`
}

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:"-"`
	Role          string       `db:"role" json:"-"`
	Access        UserAccesses `db:"access" json:"access,readonly"`
	LoginAt       *time.Time   `db:"login_at" json:"login_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"`

	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) Admin

func (u User) Admin() bool

Admin reports if this is an admin user for this site.

func (User) Bosmang

func (u User) Bosmang() 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) 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) 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"
)

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 interface{}) 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 {
	ID        int64  `db:"user_agent_id"`
	UserAgent string `db:"ua"`

	Isbot     uint8 `db:"isbot"`
	BrowserID int64 `db:"browser_id"`
	SystemID  int64 `db:"system_id"`
}

func (*UserAgent) ByID

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

func (*UserAgent) Defaults

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

func (*UserAgent) GetOrInsert

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

func (*UserAgent) Update

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

Update this useragent; re-parse it and set the new Browser/System (if any).

func (*UserAgent) Validate

func (p *UserAgent) Validate(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"`
}

UserSettings are all user preferences.

func (*UserSettings) Defaults

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

func (*UserSettings) Scan

func (ss *UserSettings) Scan(v interface{}) 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 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"`
	AsText bool   `json:"as-text"`
	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]interface{}

func NewWidget

func NewWidget(name string) Widget

func (Widget) GetSetting

func (w Widget) GetSetting(ctx context.Context, n string) interface{}

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, interface{})
	Value       interface{}
}

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 interface{})

type Widgets

type Widgets []Widget

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

func (Widgets) Get

func (w Widgets) Get(name string) Widgets

Get all widget from the list by name.

Source Files

api_token.go bosmang.go context.go export.go helper.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 tpl.go types.go update.go user.go user_agent.go

Directories

PathSynopsis
acme
bgrunPackage bgrun allows simple synchronisation of goroutines.
cmd
cmd/check
cmd/gcbenchCommands gcbench inserts random data in a GoatCounter installation for performance testing purposes.
cmd/goatcounter
cronPackage cron schedules jobs.
db
db/migrate
db/migrate/gomig
gctestPackage gctest contains testing helpers.
handlers
logscan
titlePackage title fetches a HTML page's <title>
widgets
Version
v2.1.0
Published
Dec 1, 2021
Platform
linux/amd64
Imports
67 packages
Last checked
now

Tools for package owners.