diff --git a/app/api/routes.py b/app/api/routes.py index 4eb7fbb..569761c 100644 --- a/app/api/routes.py +++ b/app/api/routes.py @@ -581,30 +581,75 @@ class IndexView(MethodView): def get(self) -> Response: """Return API information and usage examples.""" prefix = url_prefix() or "/" - return json_response( - { - "name": "FlaskPaste", - "version": VERSION, - "prefix": prefix, - "endpoints": { - f"GET {prefixed_url('/')}": "API information", - f"GET {prefixed_url('/health')}": "Health check", - f"GET {prefixed_url('/client')}": "Download CLI client", - f"GET {prefixed_url('/challenge')}": "Get PoW challenge", - f"POST {prefixed_url('/')}": "Create paste", - f"GET {prefixed_url('/pastes')}": "List your pastes (auth required)", - f"GET {prefixed_url('/')}": "Retrieve paste metadata", - f"GET {prefixed_url('//raw')}": "Retrieve raw paste content", - f"DELETE {prefixed_url('/')}": "Delete paste", + url = base_url() + difficulty = get_dynamic_difficulty() + pki_enabled = current_app.config.get("PKI_ENABLED", False) + + # Build endpoints dict + endpoints: dict[str, str] = { + f"GET {prefixed_url('/')}": "API information", + f"GET {prefixed_url('/health')}": "Health check", + f"GET {prefixed_url('/client')}": "Download CLI client (fpaste)", + f"GET {prefixed_url('/challenge')}": "Get proof-of-work challenge", + f"POST {prefixed_url('/')}": "Create paste (PoW required)", + f"GET {prefixed_url('/pastes')}": "List your pastes (cert required)", + f"GET {prefixed_url('/')}": "Get paste metadata", + f"GET {prefixed_url('//raw')}": "Get raw paste content", + f"PUT {prefixed_url('/')}": "Update paste (owner only)", + f"DELETE {prefixed_url('/')}": "Delete paste (owner only)", + f"GET {prefixed_url('/register/challenge')}": "Get registration challenge", + f"POST {prefixed_url('/register')}": "Register for client certificate", + } + + if pki_enabled: + endpoints.update( + { + f"GET {prefixed_url('/pki')}": "PKI status", + f"GET {prefixed_url('/pki/ca.crt')}": "Download CA certificate", + f"POST {prefixed_url('/pki/issue')}": "Issue client certificate", + f"GET {prefixed_url('/pki/certs')}": "List certificates", + f"POST {prefixed_url('/pki/revoke/')}": "Revoke certificate", + } + ) + + # Build response + response_data: dict[str, Any] = { + "name": "FlaskPaste", + "version": VERSION, + "prefix": prefix, + "endpoints": endpoints, + "authentication": { + "anonymous": "Create pastes only (strict limits)", + "client_cert": "Create + manage own pastes (strict limits)", + "trusted_cert": "All operations (relaxed limits)", + }, + "limits": { + "anonymous": { + "max_size": current_app.config["MAX_PASTE_SIZE_ANON"], + "rate": f"{current_app.config['RATE_LIMIT_MAX']}/min", }, - "usage": { - "raw": f"curl --data-binary @file.txt {base_url()}/", - "pipe": f"cat file.txt | curl --data-binary @- {base_url()}/", - "json": f"curl -H \"Content-Type: application/json\" -d '...' {base_url()}/", + "trusted": { + "max_size": current_app.config["MAX_PASTE_SIZE_AUTH"], + "rate": f"{current_app.config['RATE_LIMIT_MAX'] * current_app.config.get('RATE_LIMIT_AUTH_MULTIPLIER', 5)}/min", # noqa: E501 }, - "note": "Use --data-binary (not -d) to preserve newlines", - } - ) + }, + "pow": { + "enabled": difficulty > 0, + "difficulty": difficulty, + "hint": "GET /challenge, solve, submit with X-PoW-Token + X-PoW-Solution", + }, + "cli": { + "install": f"curl -o fpaste {url}/client && chmod +x fpaste", + "usage": "fpaste file.txt # encrypts by default", + }, + "usage": { + "create": f"curl -X POST --data-binary @file.txt {url}/ (with PoW headers)", + "get": f"curl {url}//raw", + "delete": f"curl -X DELETE {url}/ (with X-SSL-Client-SHA1)", + }, + } + + return json_response(response_data) def post(self) -> Response: """Create a new paste."""