Browse Source

adding telemetry to the user profile page

master
forest 1 month ago
parent
commit
e4908cc7d7
13 changed files with 134 additions and 16 deletions
  1. +1
    -1
      build-docker.sh
  2. +1
    -0
      example_config.json
  3. +2
    -0
      frontend.go
  4. +5
    -0
      frontend/alpha-profile.gotemplate.html
  5. +5
    -2
      frontend/index.gotemplate.html
  6. +2
    -1
      frontend/static/greenhouse.css
  7. +18
    -2
      frontend/static/install.sh
  8. +2
    -0
      frontend/static/uninstall.sh
  9. +80
    -0
      frontend_profile.go
  10. +8
    -1
      main.go
  11. +8
    -7
      readme/ALPHA.md
  12. +1
    -1
      readme/PRIVACY_POLICY.md
  13. +1
    -1
      telemetry.go

+ 1
- 1
build-docker.sh View File

@ -1,6 +1,6 @@
#!/bin/bash -e
VERSION="0.0.14"
VERSION="0.0.16"
rm -rf dockerbuild || true
mkdir dockerbuild


+ 1
- 0
example_config.json View File

@ -3,6 +3,7 @@
"FrontendDomain": "greenhouse-alpha.server.garden",
"EnableRegistration": false,
"HomepageMarkdownURL": "https://git.sequentialread.com/forest/greenhouse/raw/branch/master/readme/ALPHA.md",
"LokiURL": "http://loki:3100",
"DatabaseConnectionString": "host=localhost port=5432 user=postgres password=dev database=postgres sslmode=disable",
"DatabaseType": "postgres",
"DatabaseSchema": "public",


+ 2
- 0
frontend.go View File

@ -46,6 +46,7 @@ type FrontendApp struct {
TLSKey string
Domain string
WorkingDirectory string
LokiURL string
HomepageMarkdownURL string
EnableRegistration bool
Router *mux.Router
@ -74,6 +75,7 @@ func initFrontend(workingDirectory string, config *Config, model *DBModel, backe
Domain: config.FrontendDomain,
AdminTenantId: config.AdminTenantId,
HomepageMarkdownURL: config.HomepageMarkdownURL,
LokiURL: config.LokiURL,
EnableRegistration: config.EnableRegistration,
WorkingDirectory: workingDirectory,
Router: mux.NewRouter(),


+ 5
- 0
frontend/alpha-profile.gotemplate.html View File

@ -224,3 +224,8 @@
</div>
<h3>Telemetry Data for your account:</h3>
<pre class="telemetry">
{{.LokiResponseString}}
</pre>

+ 5
- 2
frontend/index.gotemplate.html View File

@ -5,9 +5,12 @@
</i>
</div>
<p>
<div class="mascot admonition">
<div class="emoji-icon">
<img src="/static/images/mascot-reading.webp"/>
</div>
For more in-depth information, see the project page: <a href="https://greenhouse.server.garden">greenhouse.server.garden</a>
</p>
</div>
<div class="homepage-markdown">
{{.DynamicContent}}


+ 2
- 1
frontend/static/greenhouse.css View File

@ -345,7 +345,8 @@ label {
margin-right: 1em;
}
.homepage-markdown pre {
.homepage-markdown pre,
pre.telemetry {
padding: 20px;
border-radius: 10px;
white-space: pre-wrap;


+ 18
- 2
frontend/static/install.sh View File

@ -12,6 +12,12 @@ footer_with_url()
printf "See: https://greenhouse-alpha.server.garden/install-greenhouse-self-hosting-software-linux\n"
printf "\n"
}
telemetry()
{
curl -sS -X POST -H "Content-Type: text/plain" "https://telemetry.greenhouse-alpha.server.garden/?type=$1&ip=auto" -d "$2"
}
telemetry "install-start" "install.sh is running"
# retrieve filesystem statistics for /run/systemd/system (whether it exists or not)
# this is "the way" to check if the operating system is using systemd or not
@ -23,6 +29,7 @@ if ! stat /run/systemd/system > /dev/null 2>&1 ; then
printf "This indicates that your operating system does not use systemd as its init system.\n"
printf "Currently this installer only supports linux with systemd.\n"
footer_with_url
telemetry "install-failure" "Did not find /run/systemd/system"
exit 1
fi
@ -37,6 +44,7 @@ if [ "$(id --user)" -ne 0 ]; then
printf "the greenhouse-daemon and install the daemon so it runs automatically \n"
printf "after your computer starts up.\n"
footer_with_url
telemetry "install-failure" "not running as root"
exit 1
fi
@ -47,6 +55,7 @@ if ! uname > /dev/null 2>&1 ; then
printf "\n"
printf "This should never happen, it is a bug!\n"
printf "\n"
telemetry "install-failure" "uname command failed. This should never happen, it is a bug!"
exit 1
fi
@ -60,6 +69,7 @@ if [ "$operating_system" != "Linux" ]; then
printf "linux computer, then this is a bug!\n"
printf "\n"
footer_with_url
telemetry "install-failure" "operating system '$operating_system' is unknown"
exit 1
fi
@ -79,6 +89,7 @@ if [ "$pkg_arch" = "error_architechture_not_found" ]; then
printf "This should probably never happen, if you are running on a 'normal'\n"
printf "linux computer, then this is a bug!\n"
printf "\n"
telemetry "install-failure" "CPU architechture '$cpu_architechture' is unknown"
exit 1
fi
@ -93,6 +104,7 @@ check_command_exists()
printf "%s is required in order to run this installer.\n" "$command_name"
printf "You should probably install it via your operating system's package manager.\n"
footer_with_url
telemetry "install-failure" "Did not find the '$command_name' command"
exit 1
fi
}
@ -136,13 +148,15 @@ update_binary()
if ! printf "%s" "$sha256sum_output" | sha256sum -c ; then
aborted_header
printf "Downloading %s binary failed.\n" "$binary_name"
printf "Expected hash: %s\n" "$daemon_sha256"
printf "Expected hash: %s\n" "$sha256sum_output"
printf "Got hash: %s\n" "$(sha256sum "$binary_target_directory/$binary_name")"
printf "\n"
printf "curl URL was: %s\n\n" "$download_url"
printf "curl output was:\n"
cat "$curl_log_file"
printf "\n\n\n"
telemetry "install-failure" "Downloading '$binary_name' binary failed. Expected $sha256sum_output, got $(sha256sum "$binary_target_directory/$binary_name")\n download_url was $download_url \n\n curl output was $curl_log_file"
exit 1
fi
fi
}
@ -290,6 +304,7 @@ if [ $attempts -eq 20 ]; then
printf "\n"
printf "\n"
printf "This is definitely a bug!\n"
telemetry "install-failure" "Greenhouse was installed, but it failed to start: \n $(systemctl status greenhouse-daemon.service) \n\n $(journalctl -u greenhouse-daemon.service -n 20 --no-pager)"
exit 1
fi
@ -298,4 +313,5 @@ printf "\n"
printf "For the command line interface, run 'greenhouse help'\n"
printf "\n"
printf "If you wish, you may also install the greenhouse-desktop GUI app at this point.\n"
footer_with_url
footer_with_url
telemetry "install-success" "greenhouse has been successfully installed!"

+ 2
- 0
frontend/static/uninstall.sh View File

@ -1,5 +1,7 @@
#!/bin/sh
curl -sS -X POST -H "Content-Type: text/plain" "https://telemetry.greenhouse-alpha.server.garden/?type=uninstall&ip=auto" -d "uninstall.sh is running..."
systemctl stop greenhouse-daemon.service
systemctl disable greenhouse-daemon.service


+ 80
- 0
frontend_profile.go View File

@ -3,9 +3,12 @@ package main
import (
"bytes"
"crypto/sha256"
"encoding/json"
"fmt"
"io/ioutil"
"log"
"net/http"
"net/url"
"regexp"
"sort"
"strconv"
@ -17,6 +20,34 @@ import (
os_from_user_agent_header "zgo.at/gadget"
)
type LokiResponse struct {
Status string `json:"status"`
Data LokiData `json:"data"`
}
type LokiData struct {
Result []LokiStream `json:"result"`
}
type LokiStream struct {
Stream map[string]string `json:"stream"`
Values lokiValues `json:"values"`
}
type lokiValues [][]string
func (values lokiValues) Len() int {
return len(values)
}
func (values lokiValues) Less(i, j int) bool {
return values[i][0] > values[j][1]
}
func (values lokiValues) Swap(i, j int) {
values[i], values[j] = values[j], values[i]
}
func registerProfileRoutes(app *FrontendApp) {
app.handleWithSession("/profile", func(responseWriter http.ResponseWriter, request *http.Request, session Session) {
@ -219,6 +250,8 @@ func registerProfileRoutes(app *FrontendApp) {
simplifiedOSName = "linux"
}
lokiResponseString := getTelemetryFromLokiForUser(app, session.Email)
data := struct {
Subdomain string
SubdomainDomain string
@ -233,6 +266,7 @@ func registerProfileRoutes(app *FrontendApp) {
BillingLimit string
HashOfSessionId string
OperatingSystem string
LokiResponseString string
}{
Subdomain: tenant.Subdomain,
SubdomainDomain: freeSubdomainDomain,
@ -247,6 +281,7 @@ func registerProfileRoutes(app *FrontendApp) {
BillingLimit: fmt.Sprintf("%.2f", float64(tenant.ServiceLimitCents)/float64(100)),
HashOfSessionId: hashOfSessionId,
OperatingSystem: simplifiedOSName,
LokiResponseString: lokiResponseString,
}
app.buildPageFromTemplateWithClass(responseWriter, request, session, "alpha-profile.html", data, "no-horizontal-margin")
})
@ -419,6 +454,51 @@ func registerProfileRoutes(app *FrontendApp) {
}
func getTelemetryFromLokiForUser(app *FrontendApp, email string) string {
lokiQuery := fmt.Sprintf("{delay=\"rt\", account=~\"^%s$\"}", email)
lokiQueryURL := fmt.Sprintf(
"%s/loki/api/v1/query_range?direction=BACKWARD&limit=1000&query=%s&start=%d&end=%d&step=900",
app.LokiURL, url.QueryEscape(lokiQuery), time.Now().Add(-1*time.Hour*24*30).UnixNano(), time.Now().Add(time.Hour).UnixNano(),
)
lokiResponseString := "HTTP Dial Error getting telemetry data\n"
lokiResponse, err := app.httpClient.Get(lokiQueryURL)
if err == nil {
lokiResponseString = fmt.Sprintf("HTTP %d error getting telemetry data", lokiResponse.StatusCode)
if lokiResponse.StatusCode < 300 {
lokiResponseString = "HTTP read error getting telemetry data"
lokiResponseBytes, err := ioutil.ReadAll(lokiResponse.Body)
if err == nil {
lokiResponseString = "JSON parse error getting telemetry data"
var lokiResponseObject LokiResponse
err = json.Unmarshal(lokiResponseBytes, &lokiResponseObject)
if err == nil {
lokiResponseString = "Loki response error getting telemetry data"
if lokiResponseObject.Status == "success" {
allValues := lokiValues{}
for _, stream := range lokiResponseObject.Data.Result {
allValues = append(allValues, stream.Values...)
}
sort.Sort(allValues)
responseStringBytes := []byte{}
for _, value := range allValues {
toAppend := "malformed data from loki\n"
if len(value) == 2 {
timestampUnixNanos, err := strconv.ParseInt(value[0], 10, 64)
if err == nil {
toAppend = fmt.Sprintf(fmt.Sprintf("%s %s\n", time.Unix(0, timestampUnixNanos).Format(time.RFC3339), value[1]))
}
}
responseStringBytes = append(responseStringBytes, []byte(toAppend)...)
}
lokiResponseString = string(responseStringBytes)
}
}
}
}
}
return lokiResponseString
}
func ByteCountSI(b int64) string {
const unit = 1000
if b < unit {


+ 8
- 1
main.go View File

@ -12,6 +12,7 @@ import (
"runtime"
"runtime/debug"
"strconv"
"strings"
"time"
"git.sequentialread.com/forest/greenhouse/pki"
@ -24,6 +25,7 @@ type Config struct {
FrontendTLSCertificate string
FrontendTLSKey string
HomepageMarkdownURL string
LokiURL string
EnableRegistration bool
AdminTenantId int
DigitalOceanAPIKey string
@ -98,7 +100,8 @@ func main() {
for {
err := backendApp.ConsumeMetrics()
if err != nil {
go postTelemetry("greenhouse-web", "", "admin", fmt.Sprintf("metric collection failed: %s", err))
firstLineOfError := strings.Split(fmt.Sprintf("metric collection failed: %s", err), "\n")[0]
go postTelemetry("greenhouse-web", "", "admin", firstLineOfError)
log.Printf("metric collection failed: %+v\n", err)
}
time.Sleep(time.Second * 6)
@ -162,6 +165,7 @@ func getConfig(workingDirectory string) *Config {
config.AdminTenantId = 1
}
lokiURL := os.Getenv("GREENHOUSE_LOKI_URL")
homepageMarkdownURL := os.Getenv("GREENHOUSE_HOMEPAGE_MARKDOWN_URL")
enableRegistrationString := os.Getenv("GREENHOUSE_ENABLE_REGISTRATION")
digitalOceanAPIKey := os.Getenv("GREENHOUSE_DIGITALOCEAN_API_KEY")
@ -178,6 +182,9 @@ func getConfig(workingDirectory string) *Config {
smtpPassword := os.Getenv("GREENHOUSE_SMTP_PASSWORD")
smtpEncryption := os.Getenv("GREENHOUSE_SMTP_ENCRYPTION")
if lokiURL != "" {
config.LokiURL = lokiURL
}
if homepageMarkdownURL != "" {
config.HomepageMarkdownURL = homepageMarkdownURL
}


+ 8
- 7
readme/ALPHA.md View File

@ -1,10 +1,3 @@
## Feedback / Bug Reports
Please use the comments form below to leave feedback or report issues.
> **Please Note** these comments are a public forum!
If you wish to contact the developer directly, you may email `forest@sequentialread.com`, contact `@forestjohnson:cyberia.club` on [matrix](https://cyberia.club/matrix), or get in touch [on the Fediverse](https://social.pixie.town/web/accounts/74557)
## Terms of Service / Privacy Policy / License
@ -20,6 +13,14 @@ This means you are allowed to modify it, create your own version of it, etc, as
GREENHOUSE AND ITS CLIENT SOFTWARE PACKAGES ARE PROVIDED “AS IS” WITHOUT WARRANTY OF ANY KIND
## Feedback / Bug Reports
Please use the comments form below to leave feedback or report issues.
> **Please Note** these comments are a public forum!
If you wish to contact the developer directly, you may email `forest@sequentialread.com`, contact `@forestjohnson:cyberia.club` on [matrix](https://cyberia.club/matrix), or get in touch [on the Fediverse](https://social.pixie.town/web/accounts/74557)
## Known Issues
```


+ 1
- 1
readme/PRIVACY_POLICY.md View File

@ -54,7 +54,7 @@ However, Greenhouse is built on top of other cloud service providers. To our use
- Any time an internet user connects to a website or service that you are operating through Greenhouse, that connection will be routed through our threshold server running on DigitalOcean's platform.
- In theory, DigitalOcean and DigitalOcean's ISP (Internet Service Provider) could collect data on which IP addresses are connecting, which domains they are connecting to, when they connected and for how long, as well as how much data they are transmitting.
- It's worth noting that DigitalOcean cannot see the contents of these connections, only the metadata.
- This is similar to the information that your ISP (Internet Service Provider) can collect on you when you run a server at home.
- This is similar to the information that your ISP (Internet Service Provider) can collect on you when you run a server at home "normally", without using greenhouse.
## GENERAL DATA PROTECTION REGULATION (GDPR)


+ 1
- 1
telemetry.go View File

@ -38,7 +38,7 @@ func postTelemetry(tyype string, ip string, account string, content string) {
account = fmt.Sprintf("&account=%s", account)
}
response, err := getTelemetryClient().Post(
fmt.Sprintf("https://greenhouse-telemetry.sequentialread.com/?type=%s%s%s", tyype, ip, account),
fmt.Sprintf("https://telemetry.greenhouse-alpha.server.garden/?type=%s%s%s", tyype, ip, account),
"text/plain",
bytes.NewBufferString(content),
)


Loading…
Cancel
Save