mirror of
https://github.com/benbusby/farside.git
synced 2025-04-20 19:08:42 +00:00

From the recent changes to twitter/X, it sounds like guest accounts are now required for nitter, which are more easily rate limited. To avoid any impact from Farside, the instances are now health checked in the nightly build using https://status.d420.de (this doesn't seem to be directly associated with the nitter maintainers, so might not be entirely future-proof).
134 lines
4.1 KiB
Elixir
134 lines
4.1 KiB
Elixir
defmodule Farside.Instances do
|
|
@fallback_suffix Application.fetch_env!(:farside, :fallback_suffix)
|
|
@update_file Application.fetch_env!(:farside, :update_file)
|
|
@service_prefix Application.fetch_env!(:farside, :service_prefix)
|
|
@headers Application.fetch_env!(:farside, :headers)
|
|
@queries Application.fetch_env!(:farside, :queries)
|
|
@debug_header "======== "
|
|
@debug_spacer " "
|
|
|
|
# These instance uptimes are inspected as part of the nightly Farside build,
|
|
# and should not be included in the constant periodic update.
|
|
@skip_service_updates ["searxng", "nitter"]
|
|
|
|
def sync() do
|
|
File.rename(@update_file, "#{@update_file}-prev")
|
|
update()
|
|
|
|
# Add UTC time of last update
|
|
CubDB.put(CubDB, "last_updated", Calendar.strftime(DateTime.utc_now(), "%c"))
|
|
end
|
|
|
|
def request(url) do
|
|
IO.puts("#{@debug_spacer}#{url}")
|
|
|
|
cond do
|
|
System.get_env("FARSIDE_TEST") ->
|
|
:good
|
|
|
|
true ->
|
|
HTTPoison.get(url, @headers)
|
|
|> then(&elem(&1, 1))
|
|
|> Map.get(:status_code)
|
|
|> case do
|
|
n when n < 300 ->
|
|
IO.puts("#{@debug_spacer}✓ [#{n}]")
|
|
:good
|
|
|
|
n ->
|
|
IO.puts("#{@debug_spacer}x [#{(n && n) || "error"}]")
|
|
:bad
|
|
end
|
|
end
|
|
end
|
|
|
|
def update() do
|
|
services_json = Application.fetch_env!(:farside, :services_json)
|
|
{:ok, file} = File.read(services_json)
|
|
{:ok, json} = Jason.decode(file)
|
|
|
|
# Loop through all instances and check each for availability
|
|
for service_json <- json do
|
|
service_atom = for {key, val} <- service_json, into: %{} do
|
|
{String.to_existing_atom(key), val}
|
|
end
|
|
|
|
service = struct(%Service{}, service_atom)
|
|
|
|
IO.puts("#{@debug_header}#{service.type}")
|
|
|
|
result = cond do
|
|
Enum.member?(@skip_service_updates, service.type) ->
|
|
get_service_vals(service.instances)
|
|
true ->
|
|
Enum.filter(service.instances, fn instance_url ->
|
|
test_url = get_test_val(instance_url)
|
|
test_path = get_test_val(service.test_url)
|
|
test_request_url = gen_validation_url(test_url, test_path)
|
|
|
|
service_url = get_service_val(instance_url)
|
|
service_path = get_service_val(service.test_url)
|
|
service_request_url = gen_validation_url(service_url, service_path)
|
|
|
|
cond do
|
|
service_url != test_url ->
|
|
service_up = request(service_request_url)
|
|
test_up = request(test_request_url)
|
|
|
|
service_up == :good && test_up == :good
|
|
true ->
|
|
request(test_request_url) == :good
|
|
end
|
|
end)
|
|
end
|
|
|
|
add_to_db(service, result)
|
|
log_results(service.type, result)
|
|
end
|
|
end
|
|
|
|
def add_to_db(service, instances) do
|
|
# Ensure only service URLs are inserted, not test URLs (separated by "|")
|
|
instances = get_service_vals(instances)
|
|
|
|
# Remove previous list of instances
|
|
CubDB.delete(CubDB, "#{@service_prefix}#{service.type}")
|
|
|
|
# Update with new list of available instances
|
|
CubDB.put(CubDB, "#{@service_prefix}#{service.type}", instances)
|
|
|
|
# Set fallback to one of the available instances,
|
|
# or the default instance if all are "down"
|
|
if Enum.count(instances) > 0 do
|
|
CubDB.put(CubDB, "#{service.type}#{@fallback_suffix}", Enum.random(instances))
|
|
else
|
|
CubDB.put(CubDB, "#{service.type}#{@fallback_suffix}", service.fallback)
|
|
end
|
|
end
|
|
|
|
def log_results(service_name, results) do
|
|
{:ok, file} = File.open(@update_file, [:append, {:delayed_write, 100, 20}])
|
|
IO.write(file, "#{service_name}: #{inspect(results)}\n")
|
|
File.close(file)
|
|
end
|
|
|
|
def gen_validation_url(url, path) do
|
|
url <> EEx.eval_string(path, query: Enum.random(@queries))
|
|
end
|
|
|
|
def get_service_vals(services) do
|
|
Enum.map(services, fn x -> get_service_val(x) end)
|
|
end
|
|
|
|
def get_service_val(service) do
|
|
String.split(service, "|") |> List.first
|
|
end
|
|
|
|
def get_test_vals(services) do
|
|
Enum.map(services, fn x -> get_test_val(x) end)
|
|
end
|
|
|
|
def get_test_val(service) do
|
|
String.split(service, "|") |> List.last
|
|
end
|
|
end
|