Initial Commit

This commit is contained in:
idanoo 2023-01-30 10:56:54 +13:00
commit 4e9199bf3e
Signed by: idanoo
GPG key ID: 387387CDBC02F132
12 changed files with 267 additions and 0 deletions

11
.env.example Normal file
View file

@ -0,0 +1,11 @@
POSTGRESQL_HOST=127.0.0.1
POSTGRESQL_USER=gomastodonstats
POSTGRESQL_PASS="superrandompassword"
POSTGRESQL_STATS_DB=gomastodonstats
TIMEZONE="Pacific/Auckland"
PIXELFED_DB_SCHEMA=pixelfed
MATRIX_DB_SCHEMA=matrix
MASTODON_DB_SCHEMA=mastodon_production
MOBILIZON_DB_SCHEMA=mobilizon_prod
PEERTUBE_DB_SCHEMA=peertube_prod

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
.env

23
README.md Normal file
View file

@ -0,0 +1,23 @@
Create readonly account with access to stats DB
```
CREATE ROLE readonly;
GRANT USAGE ON SCHEMA public TO readonly;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO readonly;
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO readonly;
CREATE USER gomastodonstats WITH PASSWORD 'superrandompassword';
GRANT readonly TO gomastodonstats;
CREATE DATABASE gomastodonstats WITH OWNER gomastodonstats;
\c gomastodonstats
CREATE TABLE IF NOT EXISTS statsdb (
id INT NOT NULL,
service VARCHAR(50) NOT NULL,
metric_name VARCHAR(50) NOT NULL,
metric_time INT NOT NULL,
metric_value INT NOT NULL,
PRIMARY KEY (id)
);
CREATE INDEX service_lookup ON statsdb USING btree (service,metric_name, metric_time);
```

View file

@ -0,0 +1,47 @@
package main
import (
gms "gomastodonstats/internal/gomastodonstats"
"log"
"os"
"github.com/joho/godotenv"
)
func main() {
err := godotenv.Load()
if err != nil {
log.Fatal("Error loading .env file")
}
// Check Postgres info exists
gms.POSTGRESQL_HOST = os.Getenv("POSTGRESQL_HOST")
if gms.POSTGRESQL_HOST == "" {
log.Fatal("POSTGRESQL_HOST not set")
}
gms.POSTGRESQL_USER = os.Getenv("POSTGRESQL_USER")
if gms.POSTGRESQL_USER == "" {
log.Fatal("POSTGRESQL_USER not set")
}
gms.POSTGRESQL_PASS = os.Getenv("POSTGRESQL_PASS")
if gms.POSTGRESQL_PASS == "" {
log.Fatal("POSTGRESQL_PASS not set")
}
gms.POSTGRESQL_STATS_DB = os.Getenv("POSTGRESQL_STATS_DB")
if gms.POSTGRESQL_STATS_DB == "" {
log.Fatal("POSTGRESQL_STATS_DB not set")
}
// Load schemas if set
gms.PIXELFED_DB_SCHEMA = os.Getenv("PIXELFED_DB_SCHEMA")
gms.MATRIX_DB_SCHEMA = os.Getenv("MATRIX_DB_SCHEMA")
gms.MASTODON_DB_SCHEMA = os.Getenv("MASTODON_DB_SCHEMA")
gms.MOBILIZON_DB_SCHEMA = os.Getenv("MOBILIZON_DB_SCHEMA")
gms.PEERTUBE_DB_SCHEMA = os.Getenv("PEERTUBE_DB_SCHEMA")
gms.Run()
}

8
go.mod Normal file
View file

@ -0,0 +1,8 @@
module gomastodonstats
go 1.19
require (
github.com/joho/godotenv v1.4.0 // indirect
github.com/lib/pq v1.10.7 // indirect
)

4
go.sum Normal file
View file

@ -0,0 +1,4 @@
github.com/joho/godotenv v1.4.0 h1:3l4+N6zfMWnkbPEXKng2o2/MR5mSwTrBih4ZEkkz1lg=
github.com/joho/godotenv v1.4.0/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
github.com/lib/pq v1.10.7 h1:p7ZhMD+KsSRozJr34udlUrhboJwWAgCg34+/ZZNvZZw=
github.com/lib/pq v1.10.7/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=

View file

@ -0,0 +1,32 @@
package gomastodonstats
// Host information
var (
POSTGRESQL_HOST string
POSTGRESQL_USER string
POSTGRESQL_PASS string
POSTGRESQL_STATS_DB string
POSTGRESQL_STATS_TABLE = "statsdb"
TIMEZONE string
// Pixelfed
PIXELFED_DB_SCHEMA string
PIXELFED_USER_QUERY = "SELECT count(*) FROM users WHERE status IS NULL;"
// Matrix
MATRIX_DB_SCHEMA string
MATRIX_USER_QUERY = "SELECT count(*) FROM users WHERE deactivated = 0;"
// Mastodon
MASTODON_DB_SCHEMA string
MASTODON_USER_QUERY = "SELECT count(*) FROM users WHERE disabled = False;"
// Mobilizon
MOBILIZON_DB_SCHEMA string
MOBILIZON_USER_QUERY = "SELECT count(*) FROM users WHERE disabled = False;"
// Peertube
PEERTUBE_DB_SCHEMA string
PEERTUBE_USER_QUERY = "SELECT count(*) FROM \"user\" WHERE blocked = False;"
)

View file

@ -0,0 +1,18 @@
package gomastodonstats
import "log"
func Run() {
log.Println("Fetching counts")
// Get Counts
metrics, err := getUserCounts()
if err != nil {
log.Fatal(err)
}
// Write to DB
persistMetrics(metrics)
// Output example
}

View file

@ -0,0 +1 @@
package gomastodonstats

View file

@ -0,0 +1,47 @@
package gomastodonstats
import (
"log"
"time"
)
// Stores out metric/row data
type metric struct {
Service string `json:"service"`
MetricName string `json:"metric_name"`
MetricTime time.Time `json:"metric_time"`
MetricValue int `json:"metric_value"`
}
func persistMetrics(metrics []metric) {
startOfDay := getStartofDay()
for _, v := range metrics {
v.MetricTime = startOfDay
err := insertValues(v)
if err != nil {
log.Println(err)
}
}
}
func getUserCounts() ([]metric, error) {
var metrics []metric
if PIXELFED_DB_SCHEMA != "" {
pfUsers, err := runIntQuery(PIXELFED_DB_SCHEMA, PIXELFED_USER_QUERY)
if err != nil {
log.Println(err)
} else {
pfMetric := metric{
Service: "pixelfed",
MetricName: "userCount",
MetricValue: pfUsers,
}
log.Printf("Pixelfed user count: %d", pfUsers)
metrics = append(metrics, pfMetric)
}
}
return metrics, nil
}

View file

@ -0,0 +1,59 @@
package gomastodonstats
import (
"database/sql"
"fmt"
_ "github.com/lib/pq"
)
// Returns valid DSL for PSQL
func getConnectionString(schema string) string {
return fmt.Sprintf(
"postgresql://%s:%s@%s/%s?sslmode=disable",
POSTGRESQL_USER,
POSTGRESQL_PASS,
POSTGRESQL_HOST,
schema,
)
}
// Returns DB connection
func getConnection(schema string) (*sql.DB, error) {
return sql.Open("postgres", getConnectionString(schema))
}
// insertMetric to stats DB
func insertValues(m metric) error {
db, err := getConnection(POSTGRESQL_STATS_DB)
if err != nil {
return err
}
_, err = db.Exec(fmt.Sprintf(
"INSERT into %s (service,metric_name,metric_time,metric_value) "+
"VALUES ($1, $2, $3, $4)",
POSTGRESQL_STATS_TABLE,
), m.Service, m.MetricName, m.MetricTime, m.MetricValue)
return err
}
func runIntQuery(schema string, q string) (int, error) {
var res int
db, err := getConnection(schema)
if err != nil {
return res, err
}
rows, err := db.Query(q)
if err != nil {
return res, err
}
defer rows.Close()
for rows.Next() {
rows.Scan(&res)
}
return res, err
}

View file

@ -0,0 +1,16 @@
package gomastodonstats
import (
"log"
"time"
)
func getStartofDay() time.Time {
localTime, err := time.LoadLocation(TIMEZONE)
if err != nil {
log.Fatal(err)
}
t := time.Now().In(localTime)
year, month, day := t.Date()
return time.Date(year, month, day, 0, 0, 0, 0, t.Location())
}