Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
직접 명령은 검토 Prompt를 거치지 않습니다. 실행하기 전에 소스를 확인하세요.
npx skills add https://github.com/tylerbutler/levee --skill new-endpoint명령은 한 줄로 유지됩니다. 복사하기 전에 가로로 스크롤해 전체 내용을 확인하세요.
로컬 사본을 원하시나요? SkillsMP에서 현재 제공할 수 있는 파일을 다운로드하세요.
SKILL.md 표시 중
SOC 직업 분류 기준
| name | new-endpoint |
| description | Create a new REST API endpoint with authentication |
Guide for adding a new REST endpoint to Levee.
Read lib/levee_web/router.ex to understand:
| Pipeline | Auth Level | Use For |
|---|---|---|
:api | None | Public endpoints (health check) |
:authenticated | Valid JWT | Basic auth, tenant validated |
:read_access | JWT + doc:read | Read document data |
:write_access | JWT + doc:write | Mutate document data |
:summary_access | JWT + summary:read | Read git-like storage |
:summary_write_access | JWT + summary:write | Write git-like storage |
Add the route to lib/levee_web/router.ex in the appropriate scope:
scope "/api", LeveeWeb do
pipe_through [:api, :authenticated, :read_access]
# Add your route
get "/your-path/:tenant_id/:id", YourController, :show
end
Create or update the controller in lib/levee_web/controllers/:
defmodule LeveeWeb.YourController do
use LeveeWeb, :controller
def show(conn, %{"tenant_id" => tenant_id, "id" => id}) do
# Access validated claims from auth plug
claims = conn.assigns[:claims]
# Verify tenant matches token (already done by auth plug)
# Implement your logic
case YourModule.get(tenant_id, id) do
{:ok, data} ->
json(conn, data)
{:error, :not_found} ->
conn
|> put_status(:not_found)
|> json(%{error: "not_found"})
end
end
end
Create tests in test/levee_web/controllers/:
defmodule LeveeWeb.YourControllerTest do
use LeveeWeb.ConnCase, async: true
@tenant_id "test-tenant"
@document_id "test-doc"
@user_id "test-user"
setup do
TenantSecrets.register_tenant(@tenant_id, "test-secret")
on_exit(fn -> TenantSecrets.unregister_tenant(@tenant_id) end)
:ok
end
describe "show/2" do
test "returns data with valid token", %{conn: conn} do
token = JWT.generate_test_token(@tenant_id, @document_id, @user_id)
conn =
conn
|> put_req_header("authorization", "Bearer #{token}")
|> get("/api/your-path/#{@tenant_id}/#{@document_id}")
assert json_response(conn, 200)
end
test "returns 401 without token", %{conn: conn} do
conn = get(conn, "/api/your-path/#{@tenant_id}/#{@document_id}")
assert json_response(conn, 401)
end
end
end
# Run tests
mix test test/levee_web/controllers/your_controller_test.exs
# Run all tests
just test-elixir
# Manual test with curl
TOKEN=$(mix run -e 'IO.puts Levee.Auth.JWT.generate_test_token("dev-tenant", "doc", "user")')
curl -H "Authorization: Bearer $TOKEN" http://localhost:4000/api/your-path/dev-tenant/doc
def action(conn, %{"tenant_id" => tenant_id, "id" => id, "sha" => sha}) do
def create(conn, %{"tenant_id" => tenant_id} = params) do
content = params["content"] # from JSON body
conn |> put_status(:created) |> json(data) # 201
conn |> put_status(:no_content) |> send_resp(204, "")
conn |> put_status(:bad_request) |> json(%{error: "invalid"})
The auth plug validates tenant matches token. Additional checks:
# Claims available after auth plug
claims = conn.assigns[:claims]
token_tenant = claims["tenantId"]
token_doc = claims["documentId"]