mirror of
https://github.com/idanoo/go-mastodon-stats
synced 2025-07-01 13:52:20 +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