From 7a2a2133cfff1147ecc70383608bcd216cbe4bd8 Mon Sep 17 00:00:00 2001 From: Russell Ballestrini Date: Tue, 7 Jan 2025 08:23:53 -0500 Subject: [PATCH] openapi for the win! modified: README.rst modified: app.py new file: openapi.yaml --- README.rst | 9 +- app.py | 4 + openapi.yaml | 472 +++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 483 insertions(+), 2 deletions(-) create mode 100644 openapi.yaml diff --git a/README.rst b/README.rst index 1cac10c..cfec1e7 100644 --- a/README.rst +++ b/README.rst @@ -32,7 +32,7 @@ Configuration via Environment - ``PYRAFILES_SECRET`` The secret key for session signing. - If missing, ``pyrafiles`` automatically generates a **random 64-character** string at runtime & log all users out. + If missing, ``pyrafiles`` automatically generates a **random 64-character** string at runtime & will log out all users out. - ``PYRAFILES_DB_URL`` Connection string for the main database. Default: ``sqlite:///main.db``. @@ -87,7 +87,7 @@ Local Setup export PYRAFILES_SECRET="YOUR_OWN_LONG_RANDOM_STRING" python main.py - If ``PYRAFILES_SECRET`` is **not** set, the app automatically generates a 64-char secret at runtime & log all users out. + If ``PYRAFILES_SECRET`` is **not** set, the app automatically generates a 64-char secret at runtime & log out all users. 5. **Access** @@ -97,6 +97,11 @@ Local Setup Example: Agent Workflow Script ============================== +First of all, everything is documented as OpenAPI for agentic flows. + +* http://localhost:6544/docs +* http://localhost:6544/openapi.yaml + Below is a sample Bash script showing how an **agent** might: 1. Start the session (to get a cookie). diff --git a/app.py b/app.py index d351dd0..8991ce8 100644 --- a/app.py +++ b/app.py @@ -1001,6 +1001,10 @@ def main(global_config=None, **settings): config.include("pyramid_jinja2") config.include("pyramid_tm") # Include pyramid_tm for transaction management + config.include("pyramid_openapi3") + config.pyramid_openapi3_spec('openapi.yaml', route='/openapi.yaml') + config.pyramid_openapi3_add_explorer(route='/docs') + # Add .html.j2 extension for Jinja2 templates config.add_jinja2_renderer(".j2") config.add_jinja2_search_path("templates", name=".j2") diff --git a/openapi.yaml b/openapi.yaml new file mode 100644 index 0000000..d3fc9ed --- /dev/null +++ b/openapi.yaml @@ -0,0 +1,472 @@ +openapi: 3.0.3 +info: + title: PyraFiles API + version: 1.0.0 + description: | + API specification for the PyraFiles application. + PyraFiles allows users to register, authenticate, upload media files, + and manage their media content. +servers: + - url: http://localhost:{port} + description: Local development server + variables: + port: + default: '6544' + - url: https://upload.unturf.com + description: prod for humans & agents to mingle. + +paths: + /: + get: + summary: Home Page + description: Displays the home page. + responses: + '200': + description: Successful response + content: + text/html: + schema: + type: string + + /auth/login: + get: + summary: Display Login Page + description: Renders the login page where users can enter their email. + responses: + '200': + description: Login page rendered + content: + text/html: + schema: + type: string + post: + summary: Process Login + description: Sends a verification code to the user's email. + requestBody: + required: true + content: + application/x-www-form-urlencoded: + schema: + type: object + properties: + email: + type: string + format: email + required: + - email + responses: + '302': + description: Redirects to the verification page + '400': + description: Bad Request (e.g., email missing) + '500': + description: Internal Server Error + + /auth/verify: + get: + summary: Display Verification Page + description: Renders the verification page where users can enter their code. + responses: + '200': + description: Verification page rendered + content: + text/html: + schema: + type: string + post: + summary: Verify User + description: Verifies the user's code and logs them in. + requestBody: + required: true + content: + application/x-www-form-urlencoded: + schema: + type: object + properties: + code: + type: string + required: + - code + responses: + '302': + description: Redirects to the home page upon successful verification + '400': + description: Bad Request (e.g., invalid code) + '500': + description: Internal Server Error + + /auth/logout: + get: + summary: Logout User + description: Logs out the current user. + responses: + '302': + description: Redirects to the home page + + /auth/profile: + get: + summary: Display User Profile + description: Shows the user's profile, including upload stats. + security: + - sessionAuth: [] + responses: + '200': + description: Profile page rendered + content: + text/html: + schema: + type: string + '403': + description: Unauthorized (user not logged in) + post: + summary: Update User Profile + description: Updates the user's profile settings. + security: + - sessionAuth: [] + requestBody: + required: true + content: + application/x-www-form-urlencoded: + schema: + type: object + properties: + enable_gravatar: + type: string + enum: ['on'] + new_username: + type: string + responses: + '302': + description: Redirects to the profile page + '400': + description: Bad Request (e.g., username already in use) + '403': + description: Unauthorized (user not logged in or guest mode) + + /auth/download_db: + get: + summary: Download User Database + description: Allows the user to download their personal database file. + security: + - sessionAuth: [] + responses: + '200': + description: Database file downloaded + content: + application/octet-stream: + schema: + type: string + format: binary + '403': + description: Unauthorized (user not logged in or unverified) + '404': + description: Database file not found + + /auth/export_user_record: + get: + summary: Export User Record + description: Exports the user's record as a JSON file. + security: + - sessionAuth: [] + responses: + '200': + description: User record JSON file downloaded + content: + application/json: + schema: + type: object + '403': + description: Unauthorized (user not logged in or unverified) + + /admin/import_user_record: + get: + summary: Display Import User Record Page + description: Renders a page to import a user record (Admin only). + security: + - sessionAuth: [] + responses: + '200': + description: Import user record page rendered + content: + text/html: + schema: + type: string + '403': + description: Forbidden (user not admin) + post: + summary: Import User Record + description: Processes uploaded user record file and imports the user (Admin only). + security: + - sessionAuth: [] + requestBody: + required: true + content: + multipart/form-data: + schema: + type: object + properties: + user_record_file: + type: string + format: binary + required: + - user_record_file + responses: + '302': + description: Redirects to the home page after successful import + '400': + description: Bad Request (e.g., invalid file) + '403': + description: Forbidden (user not admin) + + /media/upload: + get: + summary: Display Media Upload Page + description: Renders the media upload form. + security: + - sessionAuth: [] + responses: + '200': + description: Upload media page rendered + content: + text/html: + schema: + type: string + '403': + description: Unauthorized (user not logged in or unverified) + post: + summary: Upload Media + description: Processes the uploaded media file. + security: + - sessionAuth: [] + requestBody: + required: true + content: + multipart/form-data: + schema: + type: object + properties: + media_file: + type: string + format: binary + title: + type: string + is_public: + type: string + enum: ['on'] + required: + - media_file + responses: + '302': + description: Redirects to the media details page + '400': + description: Bad Request (e.g., no file uploaded, unsupported media type) + '403': + description: Unauthorized (user not logged in or unverified) + + /media/list: + get: + summary: List Public Media + description: Displays a list of public media from all users. + responses: + '200': + description: Media list page rendered + content: + text/html: + schema: + type: string + + /media/user/{user_short_id}: + get: + summary: Display User's Media + description: Shows all media uploaded by a specific user. + parameters: + - in: path + name: user_short_id + required: true + schema: + type: string + description: The short ID of the user + responses: + '200': + description: User's media page rendered + content: + text/html: + schema: + type: string + '404': + description: User not found + '500': + description: Internal Server Error + + /media/{user_short_id}/{media_short_id}/details: + get: + summary: Display Media Details + description: Shows details of a specific media item. + parameters: + - in: path + name: user_short_id + required: true + schema: + type: string + description: The short ID of the user + - in: path + name: media_short_id + required: true + schema: + type: string + description: The short ID of the media item + responses: + '200': + description: Media details page rendered + content: + text/html: + schema: + type: string + '404': + description: Media or user not found + '403': + description: Forbidden (media not public and not owner) + + /media/{user_short_id}/{media_short_id}/edit: + get: + summary: Display Media Edit Page + description: Renders a form to edit media details (owner only). + security: + - sessionAuth: [] + parameters: + - in: path + name: user_short_id + required: true + schema: + type: string + description: The short ID of the user + - in: path + name: media_short_id + required: true + schema: + type: string + description: The short ID of the media item + responses: + '200': + description: Media edit page rendered + content: + text/html: + schema: + type: string + '403': + description: Forbidden (not owner or not logged in) + '404': + description: Media not found + post: + summary: Edit Media + description: Updates the media item (owner only). + security: + - sessionAuth: [] + parameters: + - in: path + name: user_short_id + required: true + schema: + type: string + description: The short ID of the user + - in: path + name: media_short_id + required: true + schema: + type: string + description: The short ID of the media item + requestBody: + required: true + content: + multipart/form-data: + schema: + type: object + properties: + title: + type: string + is_public: + type: string + enum: ['on'] + media_file: + type: string + format: binary + responses: + '302': + description: Redirects to the media details page + '400': + description: Bad Request (e.g., file too large) + '403': + description: Forbidden (not owner) + '404': + description: Media not found + + /media/{user_short_id}/{media_short_id}/delete: + post: + summary: Delete Media + description: Deletes the media item (owner only). + security: + - sessionAuth: [] + parameters: + - in: path + name: user_short_id + required: true + schema: + type: string + description: The short ID of the user + - in: path + name: media_short_id + required: true + schema: + type: string + description: The short ID of the media item + responses: + '302': + description: Redirects to the user's media list + '403': + description: Forbidden (not owner) + '404': + description: Media not found + + /media/{user_short_id}/{media_short_id}: + get: + summary: View Media + description: Retrieves the media file for viewing or download. + parameters: + - in: path + name: user_short_id + required: true + schema: + type: string + description: The short ID of the user + - in: path + name: media_short_id + required: true + schema: + type: string + description: The short ID of the media item + - in: query + name: download + schema: + type: string + enum: ['true', 'false'] + description: Set to 'true' to trigger download + responses: + '200': + description: Media file retrieved + content: + '*/*': + schema: + type: string + format: binary + '403': + description: Forbidden (media not public and not owner) + '404': + description: Media not found + +components: + securitySchemes: + sessionAuth: + type: apiKey + in: cookie + name: session + description: Session cookie for authenticated users