1 Commits

Author SHA1 Message Date
Lewis Crichton 50b3013f53 feat: anonymous telemetry
anonymous in this case meaning we can't trace it back to a particular
user

THIS IS UNTESTED AND HEAVILY WORK IN PROGRESS.
2023-10-26 23:00:39 +01:00
5 changed files with 133 additions and 19 deletions
+2
View File
@@ -9,11 +9,13 @@ DISCORD_REDIRECT_URI=
PEPPER_SECRETS= PEPPER_SECRETS=
PEPPER_SETTINGS= PEPPER_SETTINGS=
PEPPER_TELEMETRY=
SIZE_LIMIT=33554432 SIZE_LIMIT=33554432
ALLOWED_USERS= ALLOWED_USERS=
PROMETHEUS=false PROMETHEUS=false
TELEMETRY=false
PROXY_HEADER= PROXY_HEADER=
+5
View File
@@ -1,7 +1,9 @@
# Backend # Backend
Vencord API Vencord API
## Hosting ## Hosting
The API has a Docker Compose configuration, so software-wise you shouldn't need much more than just Docker. The API has a Docker Compose configuration, so software-wise you shouldn't need much more than just Docker.
Docker is the official way of hosting the backend, and other setups (whilst technically supported) will be Docker is the official way of hosting the backend, and other setups (whilst technically supported) will be
up to you to manage. up to you to manage.
@@ -9,6 +11,7 @@ up to you to manage.
1. Clone the repository 1. Clone the repository
2. Copy `.env.example` to `.env` 2. Copy `.env.example` to `.env`
3. Configure as necessary 3. Configure as necessary
- Port and host are irrelevant since it's running in a container, but you can change them if you wish. - Port and host are irrelevant since it's running in a container, but you can change them if you wish.
- `REDIS_URI` should be changed to `redis:6379`. - `REDIS_URI` should be changed to `redis:6379`.
- `ROOT_REDIRECT` should be changed to whatever you want the `/` of the API to be set to a different site, - `ROOT_REDIRECT` should be changed to whatever you want the `/` of the API to be set to a different site,
@@ -21,7 +24,9 @@ up to you to manage.
much data a user can store. much data a user can store.
- `ALLOWED_USERS` restricts what users can use this API instance for operations like settings sync. - `ALLOWED_USERS` restricts what users can use this API instance for operations like settings sync.
- `PROMETHEUS` controls whether or not to expose the `/metrics` endpoint. - `PROMETHEUS` controls whether or not to expose the `/metrics` endpoint.
- `TELEMETRY` does not matter on a self-hosted instance, as Vencord will always point to the main instance for it.
- `PROXY_HEADER` should be used if you're running it behind a reverse proxy or another service (i.e., Cloudflare). - `PROXY_HEADER` should be used if you're running it behind a reverse proxy or another service (i.e., Cloudflare).
4. Create a `docker-compose.override.yml` that maps your ports, like so: 4. Create a `docker-compose.override.yml` that maps your ports, like so:
```yaml ```yaml
services: services:
+1
View File
@@ -21,6 +21,7 @@ var (
PEPPER_SETTINGS = os.Getenv("PEPPER_SETTINGS") PEPPER_SETTINGS = os.Getenv("PEPPER_SETTINGS")
PEPPER_SECRETS = os.Getenv("PEPPER_SECRETS") PEPPER_SECRETS = os.Getenv("PEPPER_SECRETS")
PEPPER_TELEMETRY = os.Getenv("PEPPER_TELEMETRY")
SIZE_LIMIT int // initialised in main SIZE_LIMIT int // initialised in main
+62
View File
@@ -22,6 +22,7 @@ import (
"os" "os"
"strconv" "strconv"
"strings" "strings"
"time"
"github.com/ansrivas/fiberprometheus/v2" "github.com/ansrivas/fiberprometheus/v2"
"github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2"
@@ -135,6 +136,61 @@ func main() {
return float64(count) return float64(count)
}) })
promUpdate := time.NewTicker(1 * time.Minute)
activeUsers := promauto.NewGaugeVec(prometheus.GaugeOpts{
Name: "vencord_active_users",
Help: "The total number of active Vencord users",
}, []string{"version", "operating_system"})
pluginsInUse := promauto.NewGaugeVec(prometheus.GaugeOpts{
Name: "vencord_plugins_in_use",
Help: "The total number of current plugins in use by Vencord users",
}, []string{"name"})
go func() {
for {
<-promUpdate.C
iter := g.RDB.Scan(context.Background(), 0, "telemetry:*", 0).Iterator()
userStats := make(map[string]map[string]int64)
pluginStats := make(map[string]int64)
for iter.Next(context.Background()) {
telemetryData := g.RDB.HMGet(context.Background(), iter.Val(), "version", "plugins", "operatingSystem").Val()
version := telemetryData[0].(string)
plugins := telemetryData[1].([]string)
operatingSystem := telemetryData[2].(string)
if userStats[version] == nil {
userStats[version] = make(map[string]int64)
}
userStats[version][operatingSystem]++
for _, plugin := range plugins {
pluginStats[plugin]++
}
}
if err := iter.Err(); err != nil {
panic(err)
}
for version, operatingSystems := range userStats {
for operatingSystem, count := range operatingSystems {
activeUsers.WithLabelValues(version, operatingSystem).Set(float64(count))
}
}
for plugin, count := range pluginStats {
pluginsInUse.WithLabelValues(plugin).Set(float64(count))
}
}
}()
prometheus := fiberprometheus.New("vencord") prometheus := fiberprometheus.New("vencord")
prometheus.RegisterAt(app, "/metrics") prometheus.RegisterAt(app, "/metrics")
app.Use(prometheus.Middleware) app.Use(prometheus.Middleware)
@@ -164,6 +220,12 @@ func main() {
app.Delete("/v1", requireAuth, routes.DELETE) app.Delete("/v1", requireAuth, routes.DELETE)
// #endregion // #endregion
// #region telemetry
if os.Getenv("TELEMETRY") == "true" {
app.Post("/v1/telemetry", routes.POSTTelemetry)
}
// #endregion
app.Get("/v1", routes.GET) app.Get("/v1", routes.GET)
app.Get("/", func(c *fiber.Ctx) error { app.Get("/", func(c *fiber.Ctx) error {
+44
View File
@@ -0,0 +1,44 @@
package routes
import (
"time"
"github.com/gofiber/fiber/v2"
g "github.com/vencord/backend/globals"
"github.com/vencord/backend/util"
)
type TelemetryBody struct {
Plugins []string `json:"plugins"`
Version string `json:"version"`
OperatingSystem string `json:"operatingSystem"`
}
func POSTTelemetry(c *fiber.Ctx) error {
telemetry := new(TelemetryBody)
if err := c.BodyParser(telemetry); err != nil {
return err
}
telemetryId := util.Hash(g.PEPPER_TELEMETRY + c.IP())
_, err := g.RDB.HSet(c.Context(), "telemetry:"+telemetryId, map[string]interface{}{
"plugins": telemetry.Plugins,
"version": telemetry.Version,
"operatingSystem": telemetry.OperatingSystem,
}).Result()
if err != nil {
panic(err)
}
_, err = g.RDB.Expire(c.Context(), "telemetry:"+telemetryId, 3*24*time.Hour).Result()
if err != nil {
panic(err)
}
return c.SendStatus(204)
}