Skip to content

Commit

Permalink
Add BE for user subscriptions
Browse files Browse the repository at this point in the history
  • Loading branch information
vtm9 committed Feb 11, 2024
1 parent 1357675 commit 2f64cff
Show file tree
Hide file tree
Showing 31 changed files with 222 additions and 235 deletions.
61 changes: 61 additions & 0 deletions services/app/apps/codebattle/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# App artifacts
_build
deps
db
.idea
*.ez
*.retry
*.log
tmp
*.elixir_ls/
*deployment.retry
*~
*.swp
*.swo
.vscode
**/__pycache__
# Generated on crash by the VM
erl_crash.dump

# Static artifacts
node_modules

# Since we are building assets from web/static,
# we ignore priv/static. You may want to comment
# this depending on your deployment strategy.
/priv/static

# The config/prod.secret.exs file by default contains sensitive
# data and you should not commit it into version control.
#
# Alternatively, you may comment the line below and commit the
# secrets file as long as you replace its contents by environment
# variables.
services/app/config/prod.secret.exs
tags
.vagrant
*.DS_Store
.env
.deliver/config

# Generated reports
cover
.deliver/releases/
.tern-port
*.backup
.terraform
secrets.auto.tfvars
.cache-loader
*.tfstate
*.secret.yml
.elixir_ls
google.key.json
.iml

.kube/
kubeconfig.yml

stats.json
*.hcl
services/app/priv/plts/*.plt
services/app/priv/plts/*.plt.hash
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
const subscriptionTypes = {
admin: 'admin',
banned: 'banned',
free: 'free',
premium: 'premium',
admin: 'admin',
};

export default subscriptionTypes;
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,6 @@ const UserSettingsForm = ({ onSubmit, settings }) => {
name="clan"
type="text"
placeholder="Enter your clan name"
disabled
/>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ defmodule Codebattle.Game.Context do
def create_empty_game(user_id, task) do
current_player =
user_id
|> User.get_user!()
|> User.get!()
|> Player.build()

opponent_bot =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ defmodule Codebattle.Oauth.User.GithubUser do

@spec bind(User.t(), map()) :: {:ok, User.t()} | {:error, :term}
def bind(user, profile) do
github_user = User |> Repo.get_by(github_id: profile.id)
github_user = Repo.get_by(User, github_id: profile.id)

if github_user != nil && github_user.id != user.id do
{:error, "github_id has been taken"}
Expand Down
7 changes: 4 additions & 3 deletions services/app/apps/codebattle/lib/codebattle/task_pack.ex
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ defmodule Codebattle.TaskPack do

alias Codebattle.Repo
alias Codebattle.Task
alias Codebattle.User

@states ~w(draft on_moderation active disabled)
@visibility_types ~w(hidden public)
Expand Down Expand Up @@ -61,7 +62,7 @@ defmodule Codebattle.TaskPack do
def can_see_task_pack?(task_pack, user), do: can_access_task_pack?(task_pack, user)

def can_access_task_pack?(task_pack, user) do
task_pack.creator_id == user.id || Codebattle.User.admin?(user)
task_pack.creator_id == user.id || User.admin?(user)
end

@spec get_tasks_by_pack_id(pos_integer()) :: [Codebattle.Task.t()]
Expand Down Expand Up @@ -105,8 +106,8 @@ defmodule Codebattle.TaskPack do
end

def filter_visibility(query, user) do
if Codebattle.User.admin?(user) do
Function.identity(query)
if User.admin?(user) do
query
else
from(t in query,
where: [visibility: "public", state: "active"],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ defmodule Codebattle.Tournament.Player do

@fields [
:avatar_url,
:clan,
:id,
:is_banned,
:is_bot,
Expand All @@ -28,14 +29,15 @@ defmodule Codebattle.Tournament.Player do
embedded_schema do
field(:avatar_url, :string)
field(:id, :integer)
field(:clan, :string)
field(:is_banned, :boolean, default: false)
field(:is_bot, :boolean)
field(:lang, :string)
field(:matches_ids, {:array, :integer}, default: [])
field(:name, :string)
field(:place, :integer, default: 0)
field(:rank, :integer, default: 5432)
field(:rating, :integer)
field(:place, :integer, default: 0)
field(:score, :integer, default: 0)
field(:task_ids, {:array, :integer}, default: [])
field(:team_id, :integer)
Expand Down
112 changes: 64 additions & 48 deletions services/app/apps/codebattle/lib/codebattle/user.ex
Original file line number Diff line number Diff line change
Expand Up @@ -8,61 +8,69 @@ defmodule Codebattle.User do
import Ecto.Query

alias Codebattle.Repo
alias User.SoundSettings

@type t :: %__MODULE__{}
@type raw_id :: String.t() | integer()

@guest_id 0

defmodule SoundSettings do
use Ecto.Schema

import Ecto.Changeset
@primary_key false

@derive {Jason.Encoder, only: [:level, :type]}

embedded_schema do
field(:level, :integer, default: 7)
field(:type, :string, default: "dendy")
end

def changeset(struct, params) do
cast(struct, params, [:level, :type])
end
end
@subscription_types ~w(banned free premium admin)a

@derive {Jason.Encoder,
only: [
:achievements,
:avatar_url,
:clan,
:editor_mode,
:editor_theme,
:games_played,
:github_id,
:github_name,
:id,
:inserted_at,
:is_bot,
:is_guest,
:lang,
:name,
:performance,
:rank,
:rating,
:sound_settings,
:subscription_type
]}

schema "users" do
field(:name, :string)
field(:github_name, :string)
has_many(:user_games, Codebattle.UserGame)
has_many(:games, through: [:user_games, :game])

field(:achievements, {:array, :string}, default: [])
field(:avatar_url, :string)
field(:auth_token, :string)
field(:discord_avatar, :string)
field(:discord_id, :integer)
field(:discord_name, :string)
field(:editor_mode, :string)
field(:editor_theme, :string)
field(:email, :string)
field(:firebase_uid, :string)
field(:github_id, :integer)
field(:rating, :integer, default: 1200)
field(:github_name, :string)
field(:is_bot, :boolean, default: false)
field(:lang, :string, default: "js")
field(:editor_mode, :string)
field(:editor_theme, :string)
field(:clan, :string)
field(:name, :string)
field(:public_id, :binary_id)
field(:is_bot, :boolean, default: false)
field(:rank, :integer, default: 5432)
field(:achievements, {:array, :string}, default: [])
field(:discord_name, :string)
field(:discord_id, :integer)
field(:discord_avatar, :string)
field(:firebase_uid, :string)
field(:auth_token, :string)
# level range: 0..10, types: ["standard", "silent"]
field(:rating, :integer, default: 1200)
field(:subscription_type, Ecto.Enum, values: @subscription_types)

field(:games_played, :integer, virtual: true)
field(:performance, :integer, virtual: true)
field(:is_guest, :boolean, virtual: true, default: false)
field(:avatar_url, :string)
field(:clan, :string, default: "")

embeds_one(:sound_settings, SoundSettings, on_replace: :update)

has_many(:user_games, Codebattle.UserGame)
has_many(:games, through: [:user_games, :game])

timestamps()
end

Expand All @@ -75,6 +83,7 @@ defmodule Codebattle.User do
:achievements,
:auth_token,
:avatar_url,
:clan,
:discord_avatar,
:discord_id,
:discord_name,
Expand All @@ -87,46 +96,53 @@ defmodule Codebattle.User do
:lang,
:name,
:rating,
:clan
:subscription_type
])
|> validate_required([:name])
end

def settings_changeset(model, params \\ %{}) do
model
|> cast(params, [:name, :lang])
|> cast(params, [:name, :lang, :clan])
|> cast_embed(:sound_settings)
|> unique_constraint(:name)
|> validate_length(:name, min: 3, max: 16)
|> validate_length(:name, min: 2, max: 39)
|> validate_length(:clan, min: 3, max: 31)
end

@spec create_guest :: t()
def create_guest() do
@spec build_guest() :: t()
def build_guest() do
%__MODULE__{
is_guest: true,
id: @guest_id,
name: "John Dou",
subscription_type: "free",
rating: 0,
rank: 0,
sound_settings: %SoundSettings{}
}
end

@spec admin?(t()) :: boolean()
def admin?(user) do
user.name in Application.get_env(:codebattle, :admins)
end
def admin?(%__MODULE__{subscription_type: "admin"}), do: true
def admin?(_user), do: false

@spec bot?(integer() | t()) :: boolean()
@spec bot?(integer()) :: boolean()
def bot?(user_id) when is_integer(user_id), do: user_id < 0
def bot?(user = %__MODULE__{}), do: user.is_bot

@spec guest_id() :: integer()
def guest_id(), do: @guest_id

@spec get_user!(raw_id()) :: t() | no_return
def get_user!(user_id) do
__MODULE__ |> Codebattle.Repo.get!(user_id)
@spec get!(raw_id()) :: t() | no_return()
def get!(user_id) do
Repo.get!(__MODULE__, user_id)
end

@spec get(raw_id()) :: t() | nil
def get(user_id) do
get!(user_id)
rescue
_e -> nil
end

@spec get_users_by_ids(list(raw_id())) :: list(t())
Expand Down
22 changes: 22 additions & 0 deletions services/app/apps/codebattle/lib/codebattle/user/sound_settings.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
defmodule User.SoundSettings do
use Ecto.Schema

import Ecto.Changeset
@primary_key false

@types ~w(cs dendy standard silent)

@derive {Jason.Encoder, only: [:level, :type]}

embedded_schema do
field(:level, :integer, default: 7)
field(:type, :string, default: "dendy")
end

def changeset(struct, params) do
struct
|> cast(params, [:level, :type])
|> validate_length(:level, greater_than_or_equal_to: 0, less_than_or_equal_to: 10)
|> validate_inclusion(:type, @types)
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ defmodule CodebattleWeb.ChatBotChannel do

require Logger

def join("chat_bot:" <> chat_type, _payload, socket) do
def join("chat_bot:" <> _chat_type, _payload, socket) do
{:ok, %{}, socket}
end

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ defmodule CodebattleWeb.ExtensionSocket do

def connect(_params, socket) do
# TODO: add auth for extension by token, now it works only like anonymous user
{:ok, assign(socket, :current_user, Codebattle.User.create_guest())}
{:ok, assign(socket, :current_user, Codebattle.User.build_guest())}
end

def id(_socket), do: nil
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@ defmodule CodebattleWeb.UserSocket do

case Phoenix.Token.verify(socket, "user_token", user_token, max_age: 1_000_000) do
{:ok, ^guest_id} ->
{:ok, assign(socket, current_user: User.create_guest())}
{:ok, assign(socket, current_user: User.build_guest())}

{:ok, user_id} ->
{:ok, assign(socket, :current_user, User.get_user!(user_id))}
{:ok, assign(socket, :current_user, User.get!(user_id))}

{:error, _reason} ->
:error
Expand Down
Loading

0 comments on commit 2f64cff

Please sign in to comment.