diff --git a/lib/nulla/models/user.ex b/lib/nulla/models/user.ex index b3ef7ce..ce1ce22 100644 --- a/lib/nulla/models/user.ex +++ b/lib/nulla/models/user.ex @@ -1,6 +1,7 @@ defmodule Nulla.Models.User do use Ecto.Schema import Ecto.Changeset + import Ecto.Query alias Nulla.Repo alias Nulla.Snowflake alias Nulla.Models.User @@ -8,6 +9,7 @@ defmodule Nulla.Models.User do @primary_key {:id, :integer, autogenerate: false} schema "users" do field :username, :string + field :domain, :string field :email, :string field :password, :string field :is_moderator, :boolean, default: false @@ -15,7 +17,7 @@ defmodule Nulla.Models.User do field :bio, :string field :location, :string field :birthday, :date - field :fields, :map + field :fields, {:array, :map} field :tags, {:array, :string} field :follow_approval, :boolean, default: false field :is_bot, :boolean, default: false @@ -26,6 +28,7 @@ defmodule Nulla.Models.User do field :public_key, :string field :avatar, :string field :banner, :string + field :last_active_at, :utc_datetime has_many :user_sessions, Nulla.Models.Session has_many :notes, Nulla.Models.Note @@ -39,6 +42,7 @@ defmodule Nulla.Models.User do user |> cast(attrs, [ :username, + :domain, :email, :password, :is_moderator, @@ -55,10 +59,12 @@ defmodule Nulla.Models.User do :private_key, :public_key, :avatar, - :banner + :banner, + :last_active_at ]) |> validate_required([ :username, + :domain, :email, :password, :is_moderator, @@ -75,7 +81,8 @@ defmodule Nulla.Models.User do :private_key, :public_key, :avatar, - :banner + :banner, + :last_active_at ]) end @@ -91,4 +98,23 @@ defmodule Nulla.Models.User do def get_user_by_username(username), do: Repo.get_by(User, username: username) def get_user_by_username!(username), do: Repo.get_by!(User, username: username) + + def get_total_users_count(domain) do + Repo.aggregate(from(u in User, where: u.domain == ^domain), :count, :id) + end + + def get_active_users_count(domain, days) do + cutoff = DateTime.add(DateTime.utc_now(), -days * 86400, :second) + + from(u in User, + where: u.domain == ^domain and u.last_active_at > ^cutoff + ) + |> Repo.aggregate(:count, :id) + end + + def update_last_active(user) do + user + |> Ecto.Changeset.change(last_active_at: DateTime.utc_now()) + |> Repo.update() + end end diff --git a/lib/nulla_web/controllers/nodeinfo_controller.ex b/lib/nulla_web/controllers/nodeinfo_controller.ex index 92efc88..46a3bed 100644 --- a/lib/nulla_web/controllers/nodeinfo_controller.ex +++ b/lib/nulla_web/controllers/nodeinfo_controller.ex @@ -14,11 +14,16 @@ defmodule NullaWeb.NodeinfoController do def show(conn, _params) do version = Application.spec(:nulla, :vsn) |> to_string() + instance_settings = InstanceSettings.get_instance_settings!() + domain = instance_settings.domain + total = User.get_total_users_count(domain) + month = User.get_active_users_count(domain, 30) + halfyear = User.get_active_users_count(domain, 180) users = %{ - total: 0, - month: 0, - halfyear: 0 + total: total, + month: month, + halfyear: halfyear } instance_settings = InstanceSettings.get_instance_settings!() diff --git a/priv/repo/migrations/20250530110822_create_users.exs b/priv/repo/migrations/20250530110822_create_users.exs index b597c2f..2b48cfa 100644 --- a/priv/repo/migrations/20250530110822_create_users.exs +++ b/priv/repo/migrations/20250530110822_create_users.exs @@ -5,6 +5,7 @@ defmodule Nulla.Repo.Migrations.CreateUsers do create table(:users, primary_key: false) do add :id, :bigint, primary_key: true add :username, :string, null: false, unique: true + add :domain, :string, null: false add :email, :string add :password, :string add :is_moderator, :boolean, default: false, null: false @@ -12,7 +13,7 @@ defmodule Nulla.Repo.Migrations.CreateUsers do add :bio, :text add :location, :string add :birthday, :date - add :fields, :map + add :fields, :jsonb, default: "[]", null: false add :tags, {:array, :string} add :follow_approval, :boolean, default: false, null: false add :is_bot, :boolean, default: false, null: false @@ -23,6 +24,7 @@ defmodule Nulla.Repo.Migrations.CreateUsers do add :public_key, :string, null: false add :avatar, :string add :banner, :string + add :last_active_at, :utc_datetime timestamps(type: :utc_datetime) end