First
[anni] / lib / pleroma / stats.ex
1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
4
5 defmodule Pleroma.Stats do
6   use GenServer
7
8   import Ecto.Query
9
10   alias Pleroma.CounterCache
11   alias Pleroma.Repo
12   alias Pleroma.User
13
14   @interval :timer.seconds(60)
15
16   def start_link(_) do
17     GenServer.start_link(
18       __MODULE__,
19       nil,
20       name: __MODULE__
21     )
22   end
23
24   @impl true
25   def init(_args) do
26     if Pleroma.Config.get(:env) != :test do
27       {:ok, nil, {:continue, :calculate_stats}}
28     else
29       {:ok, calculate_stat_data()}
30     end
31   end
32
33   @doc "Performs update stats"
34   def force_update do
35     GenServer.call(__MODULE__, :force_update)
36   end
37
38   @doc "Returns stats data"
39   @spec get_stats() :: %{
40           domain_count: non_neg_integer(),
41           status_count: non_neg_integer(),
42           user_count: non_neg_integer()
43         }
44   def get_stats do
45     %{stats: stats} = GenServer.call(__MODULE__, :get_state)
46
47     stats
48   end
49
50   @doc "Returns list peers"
51   @spec get_peers() :: list(String.t())
52   def get_peers do
53     %{peers: peers} = GenServer.call(__MODULE__, :get_state)
54
55     peers
56   end
57
58   @spec calculate_stat_data() :: %{
59           peers: list(),
60           stats: %{
61             domain_count: non_neg_integer(),
62             status_count: non_neg_integer(),
63             user_count: non_neg_integer()
64           }
65         }
66   def calculate_stat_data do
67     peers =
68       from(
69         u in User,
70         select: fragment("distinct split_part(?, '@', 2)", u.nickname),
71         where: u.local != ^true
72       )
73       |> Repo.all()
74       |> Enum.filter(& &1)
75
76     domain_count = Enum.count(peers)
77
78     status_count = Repo.aggregate(User.Query.build(%{local: true}), :sum, :note_count)
79
80     users_query =
81       from(u in User,
82         where: u.is_active == true,
83         where: u.local == true,
84         where: not is_nil(u.nickname),
85         where: not u.invisible
86       )
87
88     user_count = Repo.aggregate(users_query, :count, :id)
89
90     %{
91       peers: peers,
92       stats: %{
93         domain_count: domain_count,
94         status_count: status_count || 0,
95         user_count: user_count
96       }
97     }
98   end
99
100   @spec get_status_visibility_count(String.t() | nil) :: map()
101   def get_status_visibility_count(instance \\ nil) do
102     if is_nil(instance) do
103       CounterCache.get_sum()
104     else
105       CounterCache.get_by_instance(instance)
106     end
107   end
108
109   @impl true
110   def handle_continue(:calculate_stats, _) do
111     stats = calculate_stat_data()
112
113     unless Pleroma.Config.get(:env) == :test do
114       Process.send_after(self(), :run_update, @interval)
115     end
116
117     {:noreply, stats}
118   end
119
120   @impl true
121   def handle_call(:force_update, _from, _state) do
122     new_stats = calculate_stat_data()
123     {:reply, new_stats, new_stats}
124   end
125
126   @impl true
127   def handle_call(:get_state, _from, state) do
128     {:reply, state, state}
129   end
130
131   @impl true
132   def handle_info(:run_update, _) do
133     new_stats = calculate_stat_data()
134     Process.send_after(self(), :run_update, @interval)
135     {:noreply, new_stats}
136   end
137 end