Packages

phoenix_kit

1.4.5
1.7.208 1.7.207 1.7.206 1.7.205 1.7.204 1.7.203 1.7.202 1.7.201 1.7.200 1.7.199 1.7.198 1.7.197 1.7.196 1.7.194 1.7.193 1.7.192 1.7.191 1.7.190 1.7.189 1.7.187 1.7.186 1.7.185 1.7.184 1.7.183 1.7.182 1.7.181 1.7.180 1.7.179 1.7.178 1.7.177 1.7.176 1.7.175 1.7.174 1.7.173 1.7.172 1.7.171 1.7.170 1.7.169 1.7.168 1.7.167 1.7.166 1.7.165 1.7.164 1.7.162 1.7.161 1.7.160 1.7.159 1.7.157 1.7.156 1.7.155 1.7.154 1.7.153 1.7.152 1.7.151 1.7.150 1.7.149 1.7.146 1.7.145 1.7.144 1.7.143 1.7.138 1.7.133 1.7.132 1.7.131 1.7.130 1.7.128 1.7.126 1.7.125 1.7.121 1.7.120 1.7.119 1.7.118 1.7.117 1.7.116 1.7.115 1.7.114 1.7.113 1.7.112 1.7.111 1.7.110 1.7.109 1.7.108 1.7.107 1.7.106 1.7.105 1.7.104 1.7.103 1.7.102 1.7.101 1.7.100 1.7.99 1.7.98 1.7.97 1.7.96 1.7.95 1.7.94 1.7.93 1.7.92 1.7.91 1.7.90 1.7.89 1.7.88 1.7.87 1.7.86 1.7.85 1.7.84 1.7.83 1.7.82 1.7.81 1.7.80 1.7.79 1.7.78 1.7.77 1.7.76 1.7.75 1.7.74 1.7.71 1.7.70 1.7.69 1.7.66 1.7.65 1.7.64 1.7.63 1.7.62 1.7.61 1.7.59 1.7.58 1.7.57 1.7.56 1.7.55 1.7.54 1.7.53 1.7.52 1.7.51 1.7.49 1.7.44 1.7.43 1.7.42 1.7.41 1.7.39 1.7.38 1.7.37 1.7.36 1.7.34 1.7.33 1.7.31 1.7.30 1.7.29 1.7.28 1.7.27 1.7.26 1.7.25 1.7.24 1.7.23 1.7.22 1.7.21 1.7.20 1.7.19 1.7.18 1.7.17 1.7.16 1.7.15 1.7.14 1.7.13 1.7.12 1.7.11 1.7.10 1.7.9 1.7.8 1.7.7 1.7.6 1.7.5 1.7.4 1.7.3 1.7.2 1.7.1 1.7.0 1.6.20 1.6.19 1.6.18 1.6.17 1.6.16 1.6.15 1.6.14 1.6.13 1.6.12 1.6.11 1.6.10 1.6.9 1.6.8 1.6.7 1.6.6 1.6.5 1.6.4 1.6.3 1.5.2 1.5.1 1.5.0 1.4.9 1.4.8 1.4.7 1.4.6 1.4.5 1.4.4 1.4.3 1.4.2 1.4.1 1.4.0 1.3.2 1.3.1 1.3.0 1.2.10 1.2.9 1.2.8 1.2.7 1.2.5 1.2.4 1.2.2 1.2.1 1.2.0 1.1.0 1.0.0

A foundation for building Elixir Phoenix apps — SaaS, social networks, ERP systems, marketplaces, and more

Current section

Files

Jump to
phoenix_kit lib phoenix_kit users sessions.ex
Raw

lib/phoenix_kit/users/sessions.ex

defmodule PhoenixKit.Users.Sessions do
@moduledoc """
Context for managing user sessions in PhoenixKit.
This module provides functions for listing, viewing, and managing active user sessions.
It's primarily used by the admin interface to monitor and control user sessions.
## Functions
- `list_active_sessions/0` - Get all currently active sessions
- `list_user_sessions/1` - Get all active sessions for a specific user
- `get_session_info/1` - Get detailed information about a specific session
- `revoke_session/1` - Revoke a specific session token
- `revoke_user_sessions/1` - Revoke all sessions for a specific user
- `count_active_sessions/0` - Get total count of active sessions
## Session Information
Each session includes:
- User information (id, email, status)
- Session creation time
- Session token (first 8 chars for identification)
- Session age and validity status
"""
import Ecto.Query, warn: false
alias PhoenixKit.Admin.Events
alias PhoenixKit.RepoHelper, as: Repo
alias PhoenixKit.Users.Auth.{User, UserToken}
@session_validity_in_days 60
@doc """
Lists all currently active sessions with user information.
Returns a list of maps containing session and user details.
## Examples
iex> list_active_sessions()
[
%{
token_id: 123,
token_preview: "abc12345",
user: %User{id: 1, email: "user@example.com"},
created_at: ~N[2024-01-01 12:00:00],
expires_at: ~N[2024-03-02 12:00:00],
is_current: false
}
]
"""
def list_active_sessions do
from(token in UserToken,
where: token.context == "session",
where: token.inserted_at > ago(@session_validity_in_days, "day"),
join: user in User,
on: token.user_id == user.id,
select: %{
token_id: token.id,
token_preview: fragment("encode(substring(?, 1, 4), 'hex')", token.token),
user_id: user.id,
user_email: user.email,
user_is_active: user.is_active,
user_confirmed_at: user.confirmed_at,
created_at: token.inserted_at,
expires_at: fragment("? + interval '60 days'", token.inserted_at)
},
order_by: [desc: token.inserted_at]
)
|> Repo.all()
|> Enum.map(&format_session_info/1)
end
@doc """
Lists all active sessions for a specific user.
## Examples
iex> list_user_sessions(%User{id: 1})
[%{token_id: 123, user: %User{}, created_at: ~N[...], ...}]
"""
def list_user_sessions(%User{id: user_id}) do
from(token in UserToken,
where: token.context == "session",
where: token.user_id == ^user_id,
where: token.inserted_at > ago(@session_validity_in_days, "day"),
join: user in User,
on: token.user_id == user.id,
select: %{
token_id: token.id,
token_preview: fragment("encode(substring(?, 1, 4), 'hex')", token.token),
user_id: user.id,
user_email: user.email,
user_is_active: user.is_active,
user_confirmed_at: user.confirmed_at,
created_at: token.inserted_at,
expires_at: fragment("? + interval '60 days'", token.inserted_at)
},
order_by: [desc: token.inserted_at]
)
|> Repo.all()
|> Enum.map(&format_session_info/1)
end
@doc """
Gets detailed information about a specific session by token ID.
## Examples
iex> get_session_info(123)
%{token_id: 123, user: %User{}, created_at: ~N[...], ...}
iex> get_session_info(999)
nil
"""
def get_session_info(token_id) when is_integer(token_id) do
from(token in UserToken,
where: token.id == ^token_id,
where: token.context == "session",
where: token.inserted_at > ago(@session_validity_in_days, "day"),
join: user in User,
on: token.user_id == user.id,
select: %{
token_id: token.id,
token_preview: fragment("encode(substring(?, 1, 4), 'hex')", token.token),
user_id: user.id,
user_email: user.email,
user_is_active: user.is_active,
user_confirmed_at: user.confirmed_at,
created_at: token.inserted_at,
expires_at: fragment("? + interval '60 days'", token.inserted_at)
}
)
|> Repo.one()
|> case do
nil -> nil
session_data -> format_session_info(session_data)
end
end
@doc """
Revokes a specific session by token ID.
Returns :ok if successful, {:error, :not_found} if session doesn't exist.
## Examples
iex> revoke_session(123)
:ok
iex> revoke_session(999)
{:error, :not_found}
"""
def revoke_session(token_id) when is_integer(token_id) do
case Repo.delete_all(
from(token in UserToken, where: token.id == ^token_id and token.context == "session")
) do
{1, _} ->
# Broadcast session revocation event
Events.broadcast_session_revoked(token_id)
:ok
{0, _} ->
{:error, :not_found}
end
end
@doc """
Revokes all sessions for a specific user.
Returns the number of sessions revoked.
## Examples
iex> revoke_user_sessions(%User{id: 1})
3
"""
def revoke_user_sessions(%User{id: user_id}) do
{count, _} =
Repo.delete_all(
from(token in UserToken,
where: token.user_id == ^user_id and token.context == "session"
)
)
# Broadcast user sessions revocation event
if count > 0 do
Events.broadcast_user_sessions_revoked(user_id, count)
end
count
end
@doc """
Counts the total number of active sessions.
## Examples
iex> count_active_sessions()
15
"""
def count_active_sessions do
from(token in UserToken,
where: token.context == "session",
where: token.inserted_at > ago(@session_validity_in_days, "day"),
select: count(token.id)
)
|> Repo.one()
end
@doc """
Gets session statistics including total, unique users, expired sessions etc.
## Examples
iex> get_session_stats()
%{
total_active: 15,
unique_users: 8,
expired_sessions: 5,
sessions_today: 3
}
"""
def get_session_stats do
now = NaiveDateTime.utc_now()
today_start = %{now | hour: 0, minute: 0, second: 0, microsecond: {0, 0}}
# Active sessions
active_query =
from(token in UserToken,
where: token.context == "session",
where: token.inserted_at > ago(@session_validity_in_days, "day")
)
total_active = Repo.aggregate(active_query, :count, :id)
unique_users = Repo.aggregate(active_query, :count, :user_id, distinct: true)
# Sessions created today
sessions_today =
from(token in UserToken,
where: token.context == "session",
where: token.inserted_at >= ^today_start,
select: count(token.id)
)
|> Repo.one()
# Expired sessions (for cleanup reference)
expired_sessions =
from(token in UserToken,
where: token.context == "session",
where: token.inserted_at <= ago(@session_validity_in_days, "day"),
select: count(token.id)
)
|> Repo.one()
%{
total_active: total_active,
unique_users: unique_users,
expired_sessions: expired_sessions,
sessions_today: sessions_today
}
end
# Private helper to format session information
defp format_session_info(session_data) do
%{
token_id: session_data.token_id,
token_preview: session_data.token_preview,
user_id: session_data.user_id,
user_email: session_data.user_email,
user_is_active: session_data.user_is_active,
user_confirmed_at: session_data.user_confirmed_at,
created_at: session_data.created_at,
expires_at: session_data.expires_at,
age_in_days: calculate_age_in_days(session_data.created_at),
is_expired: session_expired?(session_data.created_at)
}
end
defp calculate_age_in_days(created_at) do
NaiveDateTime.diff(NaiveDateTime.utc_now(), created_at, :day)
end
defp session_expired?(created_at) do
NaiveDateTime.diff(NaiveDateTime.utc_now(), created_at, :day) >= @session_validity_in_days
end
end