mirror of
https://github.com/idanoo/go-mastodon-stats
synced 2025-07-01 05:42:19 +00:00
Initial Commit
This commit is contained in:
commit
4e9199bf3e
12 changed files with 267 additions and 0 deletions
11
.env.example
Normal file
11
.env.example
Normal 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
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
.env
|
23
README.md
Normal file
23
README.md
Normal 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);
|
||||||
|
```
|
47
cmd/gomastodonstats/main.go
Normal file
47
cmd/gomastodonstats/main.go
Normal 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
8
go.mod
Normal 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
4
go.sum
Normal 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=
|
32
internal/gomastodonstats/consts.go
Normal file
32
internal/gomastodonstats/consts.go
Normal 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;"
|
||||||
|
)
|
18
internal/gomastodonstats/main.go
Normal file
18
internal/gomastodonstats/main.go
Normal 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
|
||||||
|
}
|
1
internal/gomastodonstats/mastodon.go
Normal file
1
internal/gomastodonstats/mastodon.go
Normal file
|
@ -0,0 +1 @@
|
||||||
|
package gomastodonstats
|
47
internal/gomastodonstats/metrics.go
Normal file
47
internal/gomastodonstats/metrics.go
Normal 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
|
||||||
|
}
|
59
internal/gomastodonstats/postgresql.go
Normal file
59
internal/gomastodonstats/postgresql.go
Normal 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
|
||||||
|
}
|
16
internal/gomastodonstats/utils.go
Normal file
16
internal/gomastodonstats/utils.go
Normal 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())
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue