diff options
| author | Ben Busby <contact@benbusby.com> | 2025-01-21 13:46:29 -0700 |
|---|---|---|
| committer | Ben Busby <contact@benbusby.com> | 2025-01-21 13:46:29 -0700 |
| commit | b5bad4defc6c75b9b969658229ce5fd2f3a46107 (patch) | |
| tree | acc460a4e15669e71dc61f0df2df5a27c6d2f965 /services | |
| parent | e0e395f3c82627190897683a40e4ba28104a03f9 (diff) | |
| download | farside-b5bad4defc6c75b9b969658229ce5fd2f3a46107.tar.gz | |
Rewrite project, add daily update of services list
The project was rewritten from Elixir to Go, primarily because:
- I don't write Elixir anymore and don't want to maintain a project in a
language I no longer write
- I already write Go for other projects, including my day job, so it's
a safer bet for a project that I want to maintain long term
- Go allows me to build portable executables that will make it easier
for others to run farside on their own machines
The Go version of Farsside also has a built in task to fetch the latest
services{-full}.json file from the repo and ingest it, which makes
running a farside server a lot simpler.
It also automatically fetches the latest instance state from
https://farside.link unless configured as a primary farside node, which
will allow others to use farside without increasing traffic to all
instances that are queried by farside (just to the farside node itself).
Diffstat (limited to 'services')
| -rw-r--r-- | services/mappings.go | 110 | ||||
| -rw-r--r-- | services/services.go | 93 |
2 files changed, 203 insertions, 0 deletions
diff --git a/services/mappings.go b/services/mappings.go new file mode 100644 index 0000000..53731a6 --- /dev/null +++ b/services/mappings.go @@ -0,0 +1,110 @@ +package services + +import ( + "errors" + "math/rand" + "regexp" +) + +type RegexMapping struct { + Pattern *regexp.Regexp + Targets []string +} + +var regexMap = []RegexMapping{ + { + // YouTube + Pattern: regexp.MustCompile(`youtu(\.be|be\.com)|invidious|piped`), + Targets: []string{"piped", "invidious"}, + }, + { + // Twitter / X + Pattern: regexp.MustCompile(`twitter\.com|x\.com|nitter`), + Targets: []string{"nitter"}, + }, + { + // Reddit + Pattern: regexp.MustCompile(`reddit\.com|libreddit|redlib`), + Targets: []string{"libreddit", "redlib"}, + }, + { + // Google Search + Pattern: regexp.MustCompile(`google\.com|whoogle|searx|searxng`), + Targets: []string{"whoogle", "searx", "searxng"}, + }, + { + // Instagram + Pattern: regexp.MustCompile(`instagram\.com|proxigram`), + Targets: []string{"proxigram"}, + }, + { + // Wikipedia + Pattern: regexp.MustCompile(`wikipedia\.org|wikiless`), + Targets: []string{"wikiless"}, + }, + { + // Medium + Pattern: regexp.MustCompile(`medium\.com|scribe`), + Targets: []string{"scribe"}, + }, + { + // Odysee + Pattern: regexp.MustCompile(`odysee\.com|librarian`), + Targets: []string{"librarian"}, + }, + { + // Imgur + Pattern: regexp.MustCompile(`imgur\.com|rimgo`), + Targets: []string{"rimgo"}, + }, + { + // Google Translate + Pattern: regexp.MustCompile(`translate\.google\.com|lingva`), + Targets: []string{"lingva"}, + }, + { + // TikTok + Pattern: regexp.MustCompile(`tiktok\.com|proxitok`), + Targets: []string{"proxitok"}, + }, + { + // Fandom + Pattern: regexp.MustCompile(`fandom\.com|breezewiki`), + Targets: []string{"breezewiki"}, + }, + { + // IMDB + Pattern: regexp.MustCompile(`imdb\.com|libremdb`), + Targets: []string{"libremdb"}, + }, + { + // Quora + Pattern: regexp.MustCompile(`quora\.com|quetre`), + Targets: []string{"quetre"}, + }, + { + // GitHub + Pattern: regexp.MustCompile(`github\.com|gothub`), + Targets: []string{"gothub"}, + }, + { + // StackOverflow + Pattern: regexp.MustCompile(`stackoverflow\.com|anonymousoverflow`), + Targets: []string{"anonymousoverflow"}, + }, +} + +func MatchRequest(service string) (string, error) { + for _, mapping := range regexMap { + hasMatch := mapping.Pattern.MatchString(service) + if !hasMatch { + continue + } + + index := rand.Intn(len(mapping.Targets)) + value := mapping.Targets[index] + return value, nil + } + + return "", errors.New("no match found") +} diff --git a/services/services.go b/services/services.go new file mode 100644 index 0000000..b9e5693 --- /dev/null +++ b/services/services.go @@ -0,0 +1,93 @@ +package services + +import ( + "encoding/json" + "io" + "net/http" + "os" +) + +var ( + ServiceList []Service + FallbackMap map[string]string +) + +const ( + baseRepoLink = "https://git.sr.ht/~benbusby/farside/blob/main/" + + noCFServicesJSON = "services.json" + fullServicesJSON = "services-full.json" +) + +type Service struct { + Type string `json:"type"` + TestURL string `json:"test_url,omitempty"` + Fallback string `json:"fallback,omimtempty"` + Instances []string `json:"instances"` +} + +func ingestServicesList(servicesBytes []byte) error { + err := json.Unmarshal(servicesBytes, &ServiceList) + return err +} + +func GetServicesFileName() string { + cloudflareEnabled := false + + cfEnabledVar := os.Getenv("FARSIDE_CF_ENABLED") + if len(cfEnabledVar) > 0 && cfEnabledVar == "1" { + cloudflareEnabled = true + } + + serviceJSON := noCFServicesJSON + if cloudflareEnabled { + serviceJSON = fullServicesJSON + } + + return serviceJSON +} + + +func FetchServicesFile(serviceJSON string) ([]byte, error) { + resp, err := http.Get(baseRepoLink + serviceJSON) + if err != nil { + return nil, err + } + + defer resp.Body.Close() + + bodyBytes, err := io.ReadAll(resp.Body) + if err != nil { + return nil, err + } + + err = os.WriteFile(serviceJSON, bodyBytes, 0666) + if err != nil { + return nil, err + } + + return bodyBytes, nil +} + +func InitializeServices() error { + serviceJSON := GetServicesFileName() + fileBytes, err := os.ReadFile(serviceJSON) + if err != nil { + fileBytes, err = FetchServicesFile(serviceJSON) + if err != nil { + return err + } + } + + err = ingestServicesList(fileBytes) + if err != nil { + return err + } + + FallbackMap = make(map[string]string) + for _, serviceElement := range ServiceList { + FallbackMap[serviceElement.Type] = serviceElement.Fallback + } + + return nil +} |
