Compare commits
1 Commits
v0.0.1
...
feat/telemetry
| Author | SHA1 | Date | |
|---|---|---|---|
| 50b3013f53 |
@@ -9,11 +9,13 @@ DISCORD_REDIRECT_URI=
|
||||
|
||||
PEPPER_SECRETS=
|
||||
PEPPER_SETTINGS=
|
||||
PEPPER_TELEMETRY=
|
||||
|
||||
SIZE_LIMIT=33554432
|
||||
|
||||
ALLOWED_USERS=
|
||||
|
||||
PROMETHEUS=false
|
||||
TELEMETRY=false
|
||||
|
||||
PROXY_HEADER=
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
# Backend
|
||||
|
||||
Vencord API
|
||||
|
||||
## Hosting
|
||||
|
||||
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
|
||||
up to you to manage.
|
||||
@@ -9,19 +11,22 @@ up to you to manage.
|
||||
1. Clone the repository
|
||||
2. Copy `.env.example` to `.env`
|
||||
3. Configure as necessary
|
||||
- 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`.
|
||||
- `ROOT_REDIRECT` should be changed to whatever you want the `/` of the API to be set to a different site,
|
||||
|
||||
- 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`.
|
||||
- `ROOT_REDIRECT` should be changed to whatever you want the `/` of the API to be set to a different site,
|
||||
like your own personal homepage.
|
||||
- `DISCORD_*` should be configured with your Discord application. The redirect URI is `https://<yourdomain>/v1/oauth/callback`.
|
||||
- `PEPPER_*` should be unique values. These provide extra anonymity and make it more difficult to get user
|
||||
- `DISCORD_*` should be configured with your Discord application. The redirect URI is `https://<yourdomain>/v1/oauth/callback`.
|
||||
- `PEPPER_*` should be unique values. These provide extra anonymity and make it more difficult to get user
|
||||
info. It [is recommended](https://stackoverflow.com/a/9622855) you use at least 32 bytes of randomness, e.g. through
|
||||
`openssl rand -hex 32`.
|
||||
- `SIZE_LIMIT` is up to you, but should usually be left as default. This is for the settings sync and how
|
||||
- `SIZE_LIMIT` is up to you, but should usually be left as default. This is for the settings sync and how
|
||||
much data a user can store.
|
||||
- `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.
|
||||
- `PROXY_HEADER` should be used if you're running it behind a reverse proxy or another service (i.e., Cloudflare).
|
||||
- `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.
|
||||
- `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).
|
||||
|
||||
4. Create a `docker-compose.override.yml` that maps your ports, like so:
|
||||
```yaml
|
||||
services:
|
||||
|
||||
@@ -21,6 +21,7 @@ var (
|
||||
|
||||
PEPPER_SETTINGS = os.Getenv("PEPPER_SETTINGS")
|
||||
PEPPER_SECRETS = os.Getenv("PEPPER_SECRETS")
|
||||
PEPPER_TELEMETRY = os.Getenv("PEPPER_TELEMETRY")
|
||||
|
||||
SIZE_LIMIT int // initialised in main
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ import (
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/ansrivas/fiberprometheus/v2"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
@@ -135,6 +136,61 @@ func main() {
|
||||
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.RegisterAt(app, "/metrics")
|
||||
app.Use(prometheus.Middleware)
|
||||
@@ -164,6 +220,12 @@ func main() {
|
||||
app.Delete("/v1", requireAuth, routes.DELETE)
|
||||
// #endregion
|
||||
|
||||
// #region telemetry
|
||||
if os.Getenv("TELEMETRY") == "true" {
|
||||
app.Post("/v1/telemetry", routes.POSTTelemetry)
|
||||
}
|
||||
// #endregion
|
||||
|
||||
app.Get("/v1", routes.GET)
|
||||
|
||||
app.Get("/", func(c *fiber.Ctx) error {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
Reference in New Issue
Block a user