Packages
electric
1.0.21
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.10
1.6.9
1.6.8
1.6.7
1.6.6
1.6.5
1.6.4
1.6.3
1.6.2
1.6.1
1.6.0
1.5.1
1.5.0
1.4.16
1.4.16-beta-1
1.4.15
1.4.14
1.4.13
1.4.12
1.4.11
1.4.10
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.4
1.3.3
1.3.2
1.2.4
1.2.3
1.2.2
1.2.1
1.2.0
1.1.14
1.1.13
1.1.12
1.1.11
1.1.10
1.1.9
1.1.8
1.1.7
1.1.6
retired
1.1.5
retired
1.1.4
retired
1.1.3
retired
1.1.2
1.1.1
1.1.0
1.0.24
1.0.23
1.0.22
1.0.21
1.0.20
1.0.19
1.0.18
1.0.17
1.0.15
1.0.13
1.0.12
1.0.11
1.0.10
1.0.9
1.0.5
1.0.4
1.0.3
1.0.2
1.0.1
1.0.0
1.0.0-beta.23
1.0.0-beta.22
1.0.0-beta.20
1.0.0-beta.19
1.0.0-beta.18
1.0.0-beta.17
1.0.0-beta.16
1.0.0-beta.15
1.0.0-beta.14
1.0.0-beta.13
1.0.0-beta.12
1.0.0-beta.11
1.0.0-beta.10
1.0.0-beta.9
1.0.0-beta.8
1.0.0-beta.7
1.0.0-beta.6
1.0.0-beta.5
1.0.0-beta.4
1.0.0-beta.3
1.0.0-beta.2
1.0.0-beta.1
0.9.5
0.9.4
0.9.3
0.9.2
0.9.1
0.9.0
0.8.1
0.8.0
0.7.7
0.7.6
0.7.5
0.7.4
0.7.3
0.7.2
0.7.1
0.7.0
0.6.3
0.6.2
0.6.1
0.5.2
0.4.4
Postgres sync engine. Sync little subsets of your Postgres data into local apps and services.
Current section
Files
Jump to
Current section
Files
lib/electric/plug/router.ex
defmodule Electric.Plug.Router do
use Plug.Router, copy_opts_to_assign: :config
use Electric.Telemetry
with_telemetry Sentry.PlugCapture do
use Sentry.PlugCapture
end
alias Electric.Plug.Utils.CORSHeaderPlug
alias Electric.Plug.Utils.PassAssignToOptsPlug
plug Plug.RequestId, assign_as: :plug_request_id
plug :server_header, Electric.version()
plug :add_stack_id_to_metadata
# converts HEAD requests to GET requests
plug Plug.Head
plug RemoteIp
plug :match
plug Electric.Plug.LabelProcessPlug
plug Electric.Plug.TraceContextPlug
plug Plug.Telemetry, event_prefix: [:electric, :routing]
plug Plug.Logger
with_telemetry Sentry.PlugCapture do
plug Sentry.PlugContext
end
plug :authenticate
plug :put_cors_headers
plug :dispatch
match "/", via: [:get, :head], do: send_resp(conn, 200, "")
get "/v1/shape",
to: PassAssignToOptsPlug,
init_opts: [plug: Electric.Plug.ServeShapePlug, assign_key: :config]
delete "/v1/shape",
to: PassAssignToOptsPlug,
init_opts: [plug: Electric.Plug.DeleteShapePlug, assign_key: :config]
options "/v1/shape", to: Electric.Plug.OptionsShapePlug
get "/v1/health", to: Electric.Plug.HealthCheckPlug
match _, do: send_resp(conn, 404, "Not found")
def server_header(conn, version),
do: conn |> Plug.Conn.put_resp_header("electric-server", "ElectricSQL/#{version}")
# OPTIONS requests should not be authenticated
def authenticate(%Plug.Conn{method: "OPTIONS"} = conn, _opts), do: conn
def authenticate(%Plug.Conn{request_path: "/v1/shape"} = conn, _opts) do
api_secret = conn.assigns.config[:secret]
if is_nil(api_secret) do
# We're in insecure mode, so we don't need to authenticate
conn
else
conn = conn |> fetch_query_params()
# Keep `api_secret` for backwards compatibility
# We'll remove it when we release v2
case conn.query_params["secret"] || conn.query_params["api_secret"] do
^api_secret ->
conn
_ ->
conn
|> send_resp(401, Jason.encode!(%{message: "Unauthorized - Invalid API secret"}))
|> halt()
end
end
end
# For unmatched routes, just pass through
def authenticate(conn, _opts), do: conn
def put_cors_headers(%Plug.Conn{path_info: ["v1", "shape" | _]} = conn, _opts),
do: CORSHeaderPlug.call(conn, %{methods: ["GET", "HEAD", "DELETE", "OPTIONS"]})
def put_cors_headers(conn, _opts),
do: CORSHeaderPlug.call(conn, %{methods: ["GET", "HEAD"]})
def add_stack_id_to_metadata(conn, _) do
Logger.metadata(stack_id: conn.assigns.config[:stack_id])
Electric.Telemetry.Sentry.set_tags_context(stack_id: conn.assigns.config[:stack_id])
conn
end
end