Compare commits
1 Commits
Author | SHA1 | Date | |
---|---|---|---|
88ccb65110 |
@ -1,32 +0,0 @@
|
|||||||
name: Base CI
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [ "master" ]
|
|
||||||
pull_request:
|
|
||||||
branches: [ "master" ]
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
name: Build and test
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- name: Set up Elixir
|
|
||||||
uses: erlef/setup-beam@61e01a43a562a89bfc54c7f9a378ff67b03e4a21 # v1.16.0
|
|
||||||
with:
|
|
||||||
elixir-version: '1.14.0' # [Required] Define the Elixir version
|
|
||||||
otp-version: '25.0' # [Required] Define the Erlang/OTP version
|
|
||||||
- name: Restore dependencies cache
|
|
||||||
uses: actions/cache@v3
|
|
||||||
with:
|
|
||||||
path: deps
|
|
||||||
key: ${{ runner.os }}-mix-${{ hashFiles('**/mix.lock') }}
|
|
||||||
restore-keys: ${{ runner.os }}-mix-
|
|
||||||
- name: Install dependencies
|
|
||||||
run: mix deps.get
|
|
||||||
- name: Run tests
|
|
||||||
run: mix test
|
|
32
.github/workflows/elixir.yml
vendored
32
.github/workflows/elixir.yml
vendored
@ -1,32 +0,0 @@
|
|||||||
name: Base CI
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [ "master" ]
|
|
||||||
pull_request:
|
|
||||||
branches: [ "master" ]
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
name: Build and test
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- name: Set up Elixir
|
|
||||||
uses: erlef/setup-beam@61e01a43a562a89bfc54c7f9a378ff67b03e4a21 # v1.16.0
|
|
||||||
with:
|
|
||||||
elixir-version: '1.14.0' # [Required] Define the Elixir version
|
|
||||||
otp-version: '25.0' # [Required] Define the Erlang/OTP version
|
|
||||||
- name: Restore dependencies cache
|
|
||||||
uses: actions/cache@v3
|
|
||||||
with:
|
|
||||||
path: deps
|
|
||||||
key: ${{ runner.os }}-mix-${{ hashFiles('**/mix.lock') }}
|
|
||||||
restore-keys: ${{ runner.os }}-mix-
|
|
||||||
- name: Install dependencies
|
|
||||||
run: mix deps.get
|
|
||||||
- name: Run tests
|
|
||||||
run: mix test
|
|
11
Makefile
11
Makefile
@ -1,11 +0,0 @@
|
|||||||
up:
|
|
||||||
sudo docker compose up -d
|
|
||||||
make migrate-up
|
|
||||||
mix phx.server
|
|
||||||
|
|
||||||
migrate-up:
|
|
||||||
mix ecto.migrate
|
|
||||||
|
|
||||||
migrate-down:
|
|
||||||
mix ecto.rollback
|
|
||||||
|
|
@ -14,13 +14,6 @@ Now you can visit [`localhost:4000`](http://localhost:4000) from your browser.
|
|||||||
|
|
||||||
Ready to run in production? Please [check our deployment guides](https://hexdocs.pm/phoenix/deployment.html).
|
Ready to run in production? Please [check our deployment guides](https://hexdocs.pm/phoenix/deployment.html).
|
||||||
|
|
||||||
### Dev DB creds
|
|
||||||
From *./config/dev.exs*
|
|
||||||
Host: localhost:5432
|
|
||||||
Database: draincloud_core_dev
|
|
||||||
Username: postgres
|
|
||||||
Password: postgres
|
|
||||||
|
|
||||||
### Some phoenix related links
|
### Some phoenix related links
|
||||||
|
|
||||||
* Official website: https://www.phoenixframework.org/
|
* Official website: https://www.phoenixframework.org/
|
||||||
|
@ -9,24 +9,24 @@ services:
|
|||||||
ports:
|
ports:
|
||||||
- 5432:5432
|
- 5432:5432
|
||||||
|
|
||||||
# minio:
|
minio:
|
||||||
# image: quay.io/minio/minio:RELEASE.2024-08-29T01-40-52Z
|
image: quay.io/minio/minio:RELEASE.2024-08-29T01-40-52Z
|
||||||
# command: server --console-address ":9001" http://minio/data{1...2}
|
command: server --console-address ":9001" http://minio/data{1...2}
|
||||||
# hostname: minio
|
hostname: minio
|
||||||
# volumes:
|
volumes:
|
||||||
# - data-1:/data1
|
- data-1:/data1
|
||||||
# - data-2:/data2
|
- data-2:/data2
|
||||||
# expose:
|
expose:
|
||||||
# - "9000"
|
- "9000"
|
||||||
# - "9001"
|
- "9001"
|
||||||
# environment:
|
environment:
|
||||||
# MINIO_ROOT_USER: minioadmin
|
MINIO_ROOT_USER: minioadmin
|
||||||
# MINIO_ROOT_PASSWORD: minioadmin
|
MINIO_ROOT_PASSWORD: minioadmin
|
||||||
# healthcheck:
|
healthcheck:
|
||||||
# test: ["CMD", "mc", "ready", "local"]
|
test: ["CMD", "mc", "ready", "local"]
|
||||||
# interval: 5s
|
interval: 5s
|
||||||
# timeout: 5s
|
timeout: 5s
|
||||||
# retries: 5
|
retries: 5
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
draincore-data:
|
draincore-data:
|
||||||
|
@ -1,10 +1,6 @@
|
|||||||
defmodule DraincloudCore.Auth do
|
defmodule DraincloudCore.Auth do
|
||||||
def is_logon(_token) do
|
def is_logon(_token) do
|
||||||
# TODO here
|
# TODO here
|
||||||
false
|
true
|
||||||
end
|
|
||||||
|
|
||||||
def auth_cookies(conn = %Plug.Conn{}, user = %DrainCloudCore.Auth.Users{}) do
|
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -1,21 +0,0 @@
|
|||||||
defmodule DrainCloudCore.Auth.Session do
|
|
||||||
use Ecto.Schema
|
|
||||||
import Ecto.Changeset
|
|
||||||
import Ecto.Query
|
|
||||||
|
|
||||||
schema "sessions" do
|
|
||||||
field :token, :string
|
|
||||||
field :user_id, :id
|
|
||||||
field :user_agent, :string
|
|
||||||
field :created_at, :utc_datetime
|
|
||||||
field :expires_at, :utc_datetime
|
|
||||||
end
|
|
||||||
|
|
||||||
def changeset(session, params \\ %{}) do
|
|
||||||
session
|
|
||||||
|> cast(params, [:id, :token, :user_id, :user_agent, :created_at, :expires_at])
|
|
||||||
|> validate_required([:id, :token, :user_id, :user_agent, :created_at, :expires_at])
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
end
|
|
@ -1,45 +0,0 @@
|
|||||||
defmodule DrainCloudCore.Auth.SessionsStore do
|
|
||||||
@behaviour Plug.Session.Store
|
|
||||||
|
|
||||||
@token_len 64
|
|
||||||
|
|
||||||
alias DrainCloudCore.Auth.SessionsRepo
|
|
||||||
alias DrainCloudCore.Repo, as: Repo
|
|
||||||
|
|
||||||
def init(_opts), do: :ok
|
|
||||||
|
|
||||||
def put(conn, sid, any, opts) do
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
def get(conn, cookie, opts) do
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
def delete(conn, sid, opts) do
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
def new_session(conn, user) do
|
|
||||||
%DrainCloudCore.Auth.Session {
|
|
||||||
user_id: user.id,
|
|
||||||
token: new_token(@token_len),
|
|
||||||
user_agent: user_agent(conn),
|
|
||||||
created_at: DateTime.utc_now(),
|
|
||||||
expires_at: DateTime.add(DateTime.utc_now(), 7*24, :hour)
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
defp user_agent(conn) do
|
|
||||||
Enum.find_value(conn.req_headers, "", fn x ->
|
|
||||||
case x do
|
|
||||||
{"user-agent", agent} -> agent
|
|
||||||
_ -> nil
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
|
|
||||||
defp new_token(length) do
|
|
||||||
:crypto.strong_rand_bytes(length) |> Base.url_encode64 |> binary_part(0, length)
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,29 +0,0 @@
|
|||||||
defmodule DrainCloudCore.Auth.Users do
|
|
||||||
use Ecto.Schema
|
|
||||||
import Ecto.Changeset
|
|
||||||
import Ecto.Query
|
|
||||||
|
|
||||||
alias DrainCloudCore.Repo, as: Repo
|
|
||||||
|
|
||||||
schema "users" do
|
|
||||||
field :login, :string
|
|
||||||
field :password, :string, redact: true
|
|
||||||
field :created_at, :utc_datetime
|
|
||||||
field :updated_at, :utc_datetime
|
|
||||||
field :deleted_at, :utc_datetime
|
|
||||||
end
|
|
||||||
|
|
||||||
def changeset(user, params \\ %{}) do
|
|
||||||
user
|
|
||||||
|> cast(params, [:id, :login, :password, :updated_at, :deleted_at])
|
|
||||||
|> validate_required([:id])
|
|
||||||
end
|
|
||||||
|
|
||||||
def add_user(user) do
|
|
||||||
Repo.insert(user)
|
|
||||||
end
|
|
||||||
|
|
||||||
def password_by_login(login) do
|
|
||||||
Repo.all(from u in "users", where: u.login == ^login, select: u.password, limit: 1)
|
|
||||||
end
|
|
||||||
end
|
|
@ -10,7 +10,6 @@ defmodule DrainCloudCore.Rtc.Application do
|
|||||||
type: :supervisor
|
type: :supervisor
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
def start_link(_) do
|
def start_link(_) do
|
||||||
children = [
|
children = [
|
||||||
DrainCloudCore.Rtc
|
DrainCloudCore.Rtc
|
||||||
@ -28,32 +27,27 @@ defmodule DrainCloudCore.Rtc do
|
|||||||
use GenServer
|
use GenServer
|
||||||
|
|
||||||
# Real-time config table schemas
|
# Real-time config table schemas
|
||||||
@config_web [
|
@config_web [attributes: [
|
||||||
attributes: [
|
|
||||||
:host,
|
:host,
|
||||||
:enable_https,
|
:enable_https,
|
||||||
:port
|
:port,
|
||||||
]
|
]]
|
||||||
]
|
|
||||||
|
|
||||||
@config_pg [
|
@config_pg [attributes: [
|
||||||
attributes: [
|
|
||||||
:host,
|
:host,
|
||||||
:port,
|
:port,
|
||||||
:ssl,
|
:ssl,
|
||||||
:user,
|
:user,
|
||||||
:password,
|
:password,
|
||||||
:db
|
:db,
|
||||||
]
|
]]
|
||||||
]
|
|
||||||
|
|
||||||
@config_s3 [
|
|
||||||
attributes: [
|
@config_s3 [attributes: [
|
||||||
:host,
|
:host,
|
||||||
:port
|
:port,
|
||||||
# user / secret / secrets etc...
|
# user / secret / secrets etc...
|
||||||
]
|
]]
|
||||||
]
|
|
||||||
|
|
||||||
# def child_spec(opts) do
|
# def child_spec(opts) do
|
||||||
# %{
|
# %{
|
||||||
@ -87,7 +81,6 @@ defmodule DrainCloudCore.Rtc do
|
|||||||
def fetch_config() do
|
def fetch_config() do
|
||||||
# Mnesia.read()
|
# Mnesia.read()
|
||||||
end
|
end
|
||||||
|
|
||||||
def init() do
|
def init() do
|
||||||
Log.info("[#{__MODULE__}] start initializin RTC subsystems")
|
Log.info("[#{__MODULE__}] start initializin RTC subsystems")
|
||||||
|
|
||||||
|
@ -8,12 +8,7 @@
|
|||||||
<%= assigns[:page_title] || "DrainCloudCore" %>
|
<%= assigns[:page_title] || "DrainCloudCore" %>
|
||||||
</.live_title>
|
</.live_title>
|
||||||
<link phx-track-static rel="stylesheet" href={~p"/assets/app.css"} />
|
<link phx-track-static rel="stylesheet" href={~p"/assets/app.css"} />
|
||||||
<script
|
<script src="https://unpkg.com/htmx.org@2.0.2" integrity="sha384-Y7hw+L/jvKeWIRRkqWYfPcvVxHzVzn5REgzbawhxAuQGwX1XWe70vji+VSeHOThJ" crossorigin="anonymous"></script>
|
||||||
src="https://unpkg.com/htmx.org@2.0.2"
|
|
||||||
integrity="sha384-Y7hw+L/jvKeWIRRkqWYfPcvVxHzVzn5REgzbawhxAuQGwX1XWe70vji+VSeHOThJ"
|
|
||||||
crossorigin="anonymous"
|
|
||||||
>
|
|
||||||
</script>
|
|
||||||
<script defer phx-track-static type="text/javascript" src={~p"/assets/app.js"}>
|
<script defer phx-track-static type="text/javascript" src={~p"/assets/app.js"}>
|
||||||
</script>
|
</script>
|
||||||
</head>
|
</head>
|
||||||
|
@ -1,45 +1,12 @@
|
|||||||
defmodule DrainCloudCoreWeb.AuthController do
|
defmodule DrainCloudCoreWeb.AuthController do
|
||||||
use DrainCloudCoreWeb, :controller
|
use DrainCloudCoreWeb, :controller
|
||||||
|
alias DrainCloudCoreWeb.Request, as: Request
|
||||||
|
|
||||||
import Plug.Conn
|
def logon(conn, _params) do
|
||||||
|
if Request.hs_token?(conn) do
|
||||||
alias DrainCloudCoreWeb.AuthController.RegisterRequest, as: RegisterRequest
|
# TODO validate token here
|
||||||
alias DrainCloudCoreWeb.AuthController.LoginRequest, as: LoginRequest
|
|
||||||
alias :logger, as: Log
|
|
||||||
alias DrainCloudCore.Auth.Users , as: Repo
|
|
||||||
|
|
||||||
def register(conn, _params) do
|
|
||||||
Log.debug(
|
|
||||||
Enum.find_value(conn.req_headers, "", fn x ->
|
|
||||||
case x do
|
|
||||||
{"user-agent", agent} -> agent
|
|
||||||
_ -> nil
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
|> inspect
|
|
||||||
)
|
|
||||||
|
|
||||||
try do
|
|
||||||
RegisterRequest.from_request(conn)
|
|
||||||
|> RegisterRequest.to_model
|
|
||||||
|> Repo.add_user
|
|
||||||
send_resp(conn, 200, Jason.encode! %{ok: true})
|
|
||||||
rescue
|
|
||||||
e in RuntimeError ->
|
|
||||||
Log.error("failed to create new user: #{e}")
|
|
||||||
raise e
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def login(conn, _params) do
|
|
||||||
try do
|
|
||||||
LoginRequest.from_request(conn)
|
|
||||||
# TODO send cookies and tokens
|
|
||||||
send_resp(conn, 200, Jason.encode! %{ok: true})
|
|
||||||
rescue
|
|
||||||
e in RuntimeError ->
|
|
||||||
Log.error("failed to insert new user: #{e}")
|
|
||||||
raise e
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -1,68 +0,0 @@
|
|||||||
defmodule DraincloudCoreWeb.AuthController.LoginRequest do
|
|
||||||
@derive [Poison.Encoder, Jason.Encoder]
|
|
||||||
|
|
||||||
defstruct login: "", password: ""
|
|
||||||
|
|
||||||
alias __MODULE__, as: Request
|
|
||||||
alias DrainCloudCore.Auth.Users, as: User
|
|
||||||
alias DrainCloudCoreWeb.Errors.InvalidArgumentException, as: InvalidArgumentException
|
|
||||||
|
|
||||||
def from_request(conn = %Plug.Conn{}) do
|
|
||||||
validate_and_build(conn.params)
|
|
||||||
end
|
|
||||||
|
|
||||||
def to_model(req = %Request{}) do
|
|
||||||
%User{
|
|
||||||
login: req.login,
|
|
||||||
password: req.password
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
defp validate_and_build(req) do
|
|
||||||
validate(req)
|
|
||||||
|> build
|
|
||||||
end
|
|
||||||
|
|
||||||
defp validate(req) do
|
|
||||||
validate_login(req)
|
|
||||||
|> validate_password
|
|
||||||
end
|
|
||||||
|
|
||||||
defp validate_login(req) do
|
|
||||||
if String.length(Map.get(req, "login")) >= 3 do
|
|
||||||
req
|
|
||||||
else
|
|
||||||
raise InvalidArgumentException, %{
|
|
||||||
message: "login must be 3 symbols at minimum",
|
|
||||||
args: ["login"]
|
|
||||||
}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
defp validate_password(req) do
|
|
||||||
if String.length(Map.get(req, "password")) >= 8 do
|
|
||||||
req
|
|
||||||
else
|
|
||||||
raise InvalidArgumentException, %{
|
|
||||||
message: "password must be 8 symbols at minimum",
|
|
||||||
args: ["password"]
|
|
||||||
}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
defp build(req) do
|
|
||||||
%Request{
|
|
||||||
login: Map.get(req, "login"),
|
|
||||||
password: Map.get(req, "password") # TODO hash password
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
defimpl String.Chars, for: __MODULE__ do
|
|
||||||
def to_string(req) do
|
|
||||||
case Jason.encode(req) do
|
|
||||||
{:ok, str} -> str
|
|
||||||
{:error, msg} -> raise "failed to encode login request to string: #{msg}"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,71 +0,0 @@
|
|||||||
defmodule DrainCloudCoreWeb.AuthController.RegisterRequest do
|
|
||||||
@derive [Poison.Encoder, Jason.Encoder]
|
|
||||||
|
|
||||||
defstruct login: "", password: ""
|
|
||||||
|
|
||||||
alias __MODULE__, as: Request
|
|
||||||
alias DrainCloudCore.Auth.Users, as: User
|
|
||||||
alias DrainCloudCoreWeb.Errors.InvalidArgumentException, as: InvalidArgumentException
|
|
||||||
|
|
||||||
def from_request(conn = %Plug.Conn{}) do
|
|
||||||
# TODO remove Kernel.inspect calls
|
|
||||||
:logger.debug("[from_request] incoming request: #{Kernel.inspect(conn.params)}")
|
|
||||||
|
|
||||||
validate_and_build(conn.params)
|
|
||||||
end
|
|
||||||
|
|
||||||
def to_model(req = %Request{}) do
|
|
||||||
%User{
|
|
||||||
login: req.login,
|
|
||||||
password: req.password
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
defp validate_and_build(req) do
|
|
||||||
validate(req)
|
|
||||||
|> build
|
|
||||||
end
|
|
||||||
|
|
||||||
defp validate(req) do
|
|
||||||
validate_login(req)
|
|
||||||
|> validate_password
|
|
||||||
end
|
|
||||||
|
|
||||||
defp validate_login(req) do
|
|
||||||
if String.length(Map.get(req, "login")) >= 3 do
|
|
||||||
req
|
|
||||||
else
|
|
||||||
raise InvalidArgumentException, %{
|
|
||||||
message: "login must be 3 symbols at minimum",
|
|
||||||
args: ["login"]
|
|
||||||
}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
defp validate_password(req) do
|
|
||||||
if String.length(Map.get(req, "password")) >= 4 do
|
|
||||||
req
|
|
||||||
else
|
|
||||||
raise InvalidArgumentException, %{
|
|
||||||
message: "password must be 4 symbols at minimum",
|
|
||||||
args: ["password"]
|
|
||||||
}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
defp build(req) do
|
|
||||||
%Request{
|
|
||||||
login: Map.get(req, "login"),
|
|
||||||
password: Map.get(req, "password") # TODO hash password
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
defimpl String.Chars, for: __MODULE__ do
|
|
||||||
def to_string(req) do
|
|
||||||
case Jason.encode(req) do
|
|
||||||
{:ok, str} -> str
|
|
||||||
{:error, msg} -> raise "failed to encode register request to string: #{msg}"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,28 +0,0 @@
|
|||||||
defmodule DrainCloudCoreWeb.Errors.InvalidArgumentException do
|
|
||||||
@derive [Jason.Encoder]
|
|
||||||
|
|
||||||
alias __MODULE__, as: InvalidArgumentException
|
|
||||||
defexception [:message, :args]
|
|
||||||
|
|
||||||
@impl true
|
|
||||||
def exception(%{message: message, args: args}) do
|
|
||||||
%InvalidArgumentException{message: message, args: args}
|
|
||||||
end
|
|
||||||
|
|
||||||
@impl true
|
|
||||||
def exception(term) do
|
|
||||||
case term do
|
|
||||||
[] -> %InvalidArgumentException{}
|
|
||||||
_ -> %InvalidArgumentException{message: "Error: " <> term}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
defimpl String.Chars, for: __MODULE__ do
|
|
||||||
def to_string(err) do
|
|
||||||
case Jason.encode(err) do
|
|
||||||
{:ok, str} -> str
|
|
||||||
{:error, msg} -> raise "failed to encode register request to string: #{msg}"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
@ -7,4 +7,5 @@ defmodule DrainCloudCoreWeb.MainController do
|
|||||||
|> put_root_layout(false)
|
|> put_root_layout(false)
|
||||||
|> json(%{data: %{name: "Some Name"}})
|
|> json(%{data: %{name: "Some Name"}})
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
@ -15,6 +15,10 @@ defmodule DrainCloudCoreWeb.Endpoint do
|
|||||||
websocket: [connect_info: [session: @session_options]],
|
websocket: [connect_info: [session: @session_options]],
|
||||||
longpoll: [connect_info: [session: @session_options]]
|
longpoll: [connect_info: [session: @session_options]]
|
||||||
|
|
||||||
|
# Serve at "/" the static files from "priv/static" directory.
|
||||||
|
#
|
||||||
|
# You should set gzip to true if you are running phx.digest
|
||||||
|
# when deploying your static files in production.
|
||||||
plug Plug.Static,
|
plug Plug.Static,
|
||||||
at: "/",
|
at: "/",
|
||||||
from: :draincloud_core,
|
from: :draincloud_core,
|
||||||
|
@ -1,10 +0,0 @@
|
|||||||
defmodule DrainCloudCoreWeb.ErrorHandler do
|
|
||||||
alias DrainCloudCoreWeb.Errors.InvalidArgumentException, as: InvalidArgumentException
|
|
||||||
def handle_reason(reason = %InvalidArgumentException{}), do: Jason.encode!(reason)
|
|
||||||
|
|
||||||
def handle_reason(reason) do
|
|
||||||
:logger.error(inspect(reason))
|
|
||||||
|
|
||||||
Jason.encode!(%{error: "Oops! Something went wrong!"})
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,3 +1,24 @@
|
|||||||
defmodule DrainCloudCoreWeb.Gettext do
|
defmodule DrainCloudCoreWeb.Gettext do
|
||||||
|
@moduledoc """
|
||||||
|
A module providing Internationalization with a gettext-based API.
|
||||||
|
|
||||||
|
By using [Gettext](https://hexdocs.pm/gettext),
|
||||||
|
your module gains a set of macros for translations, for example:
|
||||||
|
|
||||||
|
import DrainCloudCoreWeb.Gettext
|
||||||
|
|
||||||
|
# Simple translation
|
||||||
|
gettext("Here is the string to translate")
|
||||||
|
|
||||||
|
# Plural translation
|
||||||
|
ngettext("Here is the string to translate",
|
||||||
|
"Here are the strings to translate",
|
||||||
|
3)
|
||||||
|
|
||||||
|
# Domain-based translation
|
||||||
|
dgettext("errors", "Here is the error message to translate")
|
||||||
|
|
||||||
|
See the [Gettext Docs](https://hexdocs.pm/gettext) for detailed usage.
|
||||||
|
"""
|
||||||
use Gettext, otp_app: :draincloud_core
|
use Gettext, otp_app: :draincloud_core
|
||||||
end
|
end
|
||||||
|
@ -1,9 +1,5 @@
|
|||||||
defmodule DrainCloudCoreWeb.Router do
|
defmodule DrainCloudCoreWeb.Router do
|
||||||
use DrainCloudCoreWeb, :router
|
use DrainCloudCoreWeb, :router
|
||||||
use Plug.ErrorHandler
|
|
||||||
import Plug.Conn
|
|
||||||
|
|
||||||
alias DrainCloudCoreWeb.ErrorHandler, as: ErrorHandler
|
|
||||||
|
|
||||||
pipeline :browser do
|
pipeline :browser do
|
||||||
plug :accepts, ["html"]
|
plug :accepts, ["html"]
|
||||||
@ -18,19 +14,24 @@ defmodule DrainCloudCoreWeb.Router do
|
|||||||
plug :accepts, ["json"]
|
plug :accepts, ["json"]
|
||||||
end
|
end
|
||||||
|
|
||||||
scope "/api", DrainCloudCoreWeb do
|
scope "/", DrainCloudCoreWeb do
|
||||||
pipe_through :api
|
pipe_through :api
|
||||||
|
|
||||||
post "/register", AuthController, :register
|
get "/api", MainController, :test
|
||||||
end
|
end
|
||||||
|
|
||||||
@impl Plug.ErrorHandler
|
# Other scopes may use custom stacks.
|
||||||
def handle_errors(conn, %{kind: _kind, reason: reason, stack: _stack}) do
|
# scope "/api", DrainCloudCoreWeb do
|
||||||
|
# pipe_through :api
|
||||||
send_resp(conn, 500, ErrorHandler.handle_reason(reason))
|
# end
|
||||||
end
|
|
||||||
|
|
||||||
|
# Enable LiveDashboard in development
|
||||||
if Application.compile_env(:draincloud_core, :dev_routes) do
|
if Application.compile_env(:draincloud_core, :dev_routes) do
|
||||||
|
# If you want to use the LiveDashboard in production, you should put
|
||||||
|
# it behind authentication and allow only admins to access it.
|
||||||
|
# If your application does not have an admins-only section yet,
|
||||||
|
# you can use Plug.BasicAuth to set up some basic authentication
|
||||||
|
# as long as you are also using SSL (which you should anyway).
|
||||||
import Phoenix.LiveDashboard.Router
|
import Phoenix.LiveDashboard.Router
|
||||||
|
|
||||||
scope "/dev" do
|
scope "/dev" do
|
||||||
|
1
mix.exs
1
mix.exs
@ -44,7 +44,6 @@ defmodule DrainCloudCore.MixProject do
|
|||||||
{:phoenix_live_dashboard, "~> 0.8.3"},
|
{:phoenix_live_dashboard, "~> 0.8.3"},
|
||||||
{:esbuild, "~> 0.8", runtime: Mix.env() == :dev},
|
{:esbuild, "~> 0.8", runtime: Mix.env() == :dev},
|
||||||
{:tailwind, "~> 0.2", runtime: Mix.env() == :dev},
|
{:tailwind, "~> 0.2", runtime: Mix.env() == :dev},
|
||||||
{:poison, "~> 6.0"},
|
|
||||||
{:heroicons,
|
{:heroicons,
|
||||||
github: "tailwindlabs/heroicons",
|
github: "tailwindlabs/heroicons",
|
||||||
tag: "v2.1.1",
|
tag: "v2.1.1",
|
||||||
|
1
mix.lock
1
mix.lock
@ -27,7 +27,6 @@
|
|||||||
"plug": {:hex, :plug, "1.16.1", "40c74619c12f82736d2214557dedec2e9762029b2438d6d175c5074c933edc9d", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "a13ff6b9006b03d7e33874945b2755253841b238c34071ed85b0e86057f8cddc"},
|
"plug": {:hex, :plug, "1.16.1", "40c74619c12f82736d2214557dedec2e9762029b2438d6d175c5074c933edc9d", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "a13ff6b9006b03d7e33874945b2755253841b238c34071ed85b0e86057f8cddc"},
|
||||||
"plug_cowboy": {:hex, :plug_cowboy, "2.7.1", "87677ffe3b765bc96a89be7960f81703223fe2e21efa42c125fcd0127dd9d6b2", [:mix], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:cowboy_telemetry, "~> 0.3", [hex: :cowboy_telemetry, repo: "hexpm", optional: false]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "02dbd5f9ab571b864ae39418db7811618506256f6d13b4a45037e5fe78dc5de3"},
|
"plug_cowboy": {:hex, :plug_cowboy, "2.7.1", "87677ffe3b765bc96a89be7960f81703223fe2e21efa42c125fcd0127dd9d6b2", [:mix], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:cowboy_telemetry, "~> 0.3", [hex: :cowboy_telemetry, repo: "hexpm", optional: false]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "02dbd5f9ab571b864ae39418db7811618506256f6d13b4a45037e5fe78dc5de3"},
|
||||||
"plug_crypto": {:hex, :plug_crypto, "2.1.0", "f44309c2b06d249c27c8d3f65cfe08158ade08418cf540fd4f72d4d6863abb7b", [:mix], [], "hexpm", "131216a4b030b8f8ce0f26038bc4421ae60e4bb95c5cf5395e1421437824c4fa"},
|
"plug_crypto": {:hex, :plug_crypto, "2.1.0", "f44309c2b06d249c27c8d3f65cfe08158ade08418cf540fd4f72d4d6863abb7b", [:mix], [], "hexpm", "131216a4b030b8f8ce0f26038bc4421ae60e4bb95c5cf5395e1421437824c4fa"},
|
||||||
"poison": {:hex, :poison, "6.0.0", "9bbe86722355e36ffb62c51a552719534257ba53f3271dacd20fbbd6621a583a", [:mix], [{:decimal, "~> 2.1", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "bb9064632b94775a3964642d6a78281c07b7be1319e0016e1643790704e739a2"},
|
|
||||||
"postgrex": {:hex, :postgrex, "0.19.1", "73b498508b69aded53907fe48a1fee811be34cc720e69ef4ccd568c8715495ea", [:mix], [{:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "8bac7885a18f381e091ec6caf41bda7bb8c77912bb0e9285212829afe5d8a8f8"},
|
"postgrex": {:hex, :postgrex, "0.19.1", "73b498508b69aded53907fe48a1fee811be34cc720e69ef4ccd568c8715495ea", [:mix], [{:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "8bac7885a18f381e091ec6caf41bda7bb8c77912bb0e9285212829afe5d8a8f8"},
|
||||||
"ranch": {:hex, :ranch, "1.8.0", "8c7a100a139fd57f17327b6413e4167ac559fbc04ca7448e9be9057311597a1d", [:make, :rebar3], [], "hexpm", "49fbcfd3682fab1f5d109351b61257676da1a2fdbe295904176d5e521a2ddfe5"},
|
"ranch": {:hex, :ranch, "1.8.0", "8c7a100a139fd57f17327b6413e4167ac559fbc04ca7448e9be9057311597a1d", [:make, :rebar3], [], "hexpm", "49fbcfd3682fab1f5d109351b61257676da1a2fdbe295904176d5e521a2ddfe5"},
|
||||||
"tailwind": {:hex, :tailwind, "0.2.3", "277f08145d407de49650d0a4685dc062174bdd1ae7731c5f1da86163a24dfcdb", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}], "hexpm", "8e45e7a34a676a7747d04f7913a96c770c85e6be810a1d7f91e713d3a3655b5d"},
|
"tailwind": {:hex, :tailwind, "0.2.3", "277f08145d407de49650d0a4685dc062174bdd1ae7731c5f1da86163a24dfcdb", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}], "hexpm", "8e45e7a34a676a7747d04f7913a96c770c85e6be810a1d7f91e713d3a3655b5d"},
|
||||||
|
7
priv/repo/migrations/20240901221317_users.exs
Normal file
7
priv/repo/migrations/20240901221317_users.exs
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
defmodule DrainCloudCore.Repo.Migrations.Users do
|
||||||
|
use Ecto.Migration
|
||||||
|
|
||||||
|
def change do
|
||||||
|
# TODO users table migration
|
||||||
|
end
|
||||||
|
end
|
@ -1,15 +0,0 @@
|
|||||||
defmodule DrainCloudCore.Repo.Migrations.AddUsers do
|
|
||||||
use Ecto.Migration
|
|
||||||
|
|
||||||
def change do
|
|
||||||
create table(:users) do
|
|
||||||
add :login, :string, size: 120
|
|
||||||
add :password, :string
|
|
||||||
add :created_at, :utc_datetime, null: false, default: fragment("CURRENT_TIMESTAMP")
|
|
||||||
add :updated_at, :utc_datetime, null: false, default: fragment("CURRENT_TIMESTAMP")
|
|
||||||
add :deleted_at, :utc_datetime, null: true, default: nil
|
|
||||||
end
|
|
||||||
|
|
||||||
create unique_index(:users, :login)
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,15 +0,0 @@
|
|||||||
defmodule DrainCloudCore.Repo.Migrations.Sessions do
|
|
||||||
use Ecto.Migration
|
|
||||||
|
|
||||||
def change do
|
|
||||||
create table(:sessions) do
|
|
||||||
add :token, :string
|
|
||||||
add :user_id, references("users", on_delete: :nothing)
|
|
||||||
add :user_agent, :string
|
|
||||||
add :created_at, :utc_datetime
|
|
||||||
add :expires_at, :utc_datetime
|
|
||||||
end
|
|
||||||
|
|
||||||
create unique_index(:sessions, :token)
|
|
||||||
end
|
|
||||||
end
|
|
5
schemas/users.sql
Normal file
5
schemas/users.sql
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
create table users (
|
||||||
|
id bigserial,
|
||||||
|
login varchar(50),
|
||||||
|
password varchar(256)
|
||||||
|
);
|
@ -9,7 +9,6 @@ defmodule DrainCloudCoreWeb.ErrorHTMLTest do
|
|||||||
end
|
end
|
||||||
|
|
||||||
test "renders 500.html" do
|
test "renders 500.html" do
|
||||||
assert render_to_string(DrainCloudCoreWeb.ErrorHTML, "500", "html", []) ==
|
assert render_to_string(DrainCloudCoreWeb.ErrorHTML, "500", "html", []) == "Internal Server Error"
|
||||||
"Internal Server Error"
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -2,9 +2,7 @@ defmodule DrainCloudCoreWeb.ErrorJSONTest do
|
|||||||
use DrainCloudCoreWeb.ConnCase, async: true
|
use DrainCloudCoreWeb.ConnCase, async: true
|
||||||
|
|
||||||
test "renders 404" do
|
test "renders 404" do
|
||||||
assert DrainCloudCoreWeb.ErrorJSON.render("404.json", %{}) == %{
|
assert DrainCloudCoreWeb.ErrorJSON.render("404.json", %{}) == %{errors: %{detail: "Not Found"}}
|
||||||
errors: %{detail: "Not Found"}
|
|
||||||
}
|
|
||||||
end
|
end
|
||||||
|
|
||||||
test "renders 500" do
|
test "renders 500" do
|
||||||
|
Loading…
Reference in New Issue
Block a user