mirror of
https://github.com/wizarrrr/wizarr.git
synced 2025-12-23 15:51:07 -05:00
Complete Flask-RESTX migration for automatic API documentation
✅ Replaced manual API documentation with Flask-RESTX OpenAPI generation ✅ All endpoints now auto-generate Swagger UI at /api/docs/ ✅ Maintained backward compatibility - all existing API functionality preserved ✅ Added comprehensive OpenAPI models for request/response validation Changes: - app/blueprints/api/api_routes.py: Migrated from Flask routes to Flask-RESTX Resource classes - app/blueprints/api/models.py: Created OpenAPI schema definitions - app/extensions.py: Added Flask-RESTX configuration with API key security - pyproject.toml: Added flask-restx>=1.3.0 dependency - README.md: Updated API documentation to point to interactive Swagger UI - docs/API.md: Simplified to quick-start guide pointing to automatic docs
This commit is contained in:
@@ -47,9 +47,10 @@ Check out our documentation for how to install and run Wizarr:
|
||||
|
||||
## 🔧 API Documentation
|
||||
|
||||
Wizarr provides a comprehensive REST API for automation and integration. Check out the complete API documentation:
|
||||
Wizarr provides a comprehensive REST API for automation and integration with **automatic OpenAPI/Swagger documentation**:
|
||||
|
||||
📖 [API Documentation](docs/API.md)
|
||||
📖 **Interactive API Documentation**: `http://your-wizarr-instance/api/docs/`
|
||||
📋 **OpenAPI Specification**: `http://your-wizarr-instance/api/swagger.json`
|
||||
|
||||
---
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -6,6 +6,7 @@ from flask_limiter import Limiter
|
||||
from flask_limiter.util import get_remote_address
|
||||
from flask_login import LoginManager
|
||||
from flask_migrate import Migrate
|
||||
from flask_restx import Api
|
||||
from flask_sqlalchemy import SQLAlchemy
|
||||
|
||||
from flask_session import Session
|
||||
@@ -25,6 +26,27 @@ limiter = Limiter(
|
||||
enabled=False, # Explicitly disabled by default
|
||||
)
|
||||
|
||||
# Initialize Flask-RESTX API with OpenAPI configuration
|
||||
# This will be initialized later with the blueprint in api_routes.py
|
||||
api = Api(
|
||||
title="Wizarr API",
|
||||
version="2.2.1",
|
||||
description="Multi-server invitation manager for Plex, Jellyfin, Emby & AudiobookShelf",
|
||||
doc="/docs/", # Swagger UI will be available at /api/docs/
|
||||
validate=True,
|
||||
ordered=True,
|
||||
)
|
||||
|
||||
# Define API key security scheme for OpenAPI
|
||||
api.authorizations = {
|
||||
"apikey": {
|
||||
"type": "apiKey",
|
||||
"in": "header",
|
||||
"name": "X-API-Key",
|
||||
"description": "API key required for all endpoints",
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
# Initialize with app
|
||||
def init_extensions(app):
|
||||
@@ -88,6 +110,7 @@ def init_extensions(app):
|
||||
db.init_app(app)
|
||||
migrate.init_app(app, db)
|
||||
limiter.init_app(app)
|
||||
# Flask-RESTX API will be initialized with the blueprint
|
||||
|
||||
|
||||
@login_manager.user_loader
|
||||
|
||||
665
docs/API.md
665
docs/API.md
@@ -1,577 +1,62 @@
|
||||
# Wizarr API Documentation
|
||||
|
||||
This document provides comprehensive documentation for the Wizarr API endpoints, including authentication, request/response formats, and usage examples.
|
||||
Wizarr provides a comprehensive REST API with **automatic OpenAPI/Swagger documentation**.
|
||||
|
||||
## Table of Contents
|
||||
## Interactive Documentation
|
||||
|
||||
- [Authentication](#authentication)
|
||||
- [API Endpoints](#api-endpoints)
|
||||
- [Status](#status)
|
||||
- [Users](#users)
|
||||
- [Invitations](#invitations)
|
||||
- [Libraries](#libraries)
|
||||
- [Servers](#servers)
|
||||
- [API Keys](#api-keys)
|
||||
- [Error Handling](#error-handling)
|
||||
- [Rate Limiting](#rate-limiting)
|
||||
- [Examples](#examples)
|
||||
- **📖 Swagger UI**: `http://your-wizarr-instance/api/docs/`
|
||||
- **📋 OpenAPI Spec**: `http://your-wizarr-instance/api/swagger.json`
|
||||
|
||||
## Authentication
|
||||
The interactive documentation provides:
|
||||
- Real-time API testing interface
|
||||
- Complete request/response schemas
|
||||
- Authentication examples
|
||||
- Error code explanations
|
||||
|
||||
All API endpoints require authentication using an API key. API keys can be created through the Wizarr web interface under Settings → API Keys.
|
||||
## Quick Start
|
||||
|
||||
### API Key Header
|
||||
### Authentication
|
||||
|
||||
Include your API key in the request headers:
|
||||
|
||||
```
|
||||
X-API-Key: your_api_key_here
|
||||
```
|
||||
|
||||
### Example Request
|
||||
All API endpoints require authentication using an API key:
|
||||
|
||||
```bash
|
||||
curl -H "X-API-Key: your_api_key_here" https://your-wizarr-instance.com/api/status
|
||||
curl -H "X-API-Key: your_api_key_here" \
|
||||
http://your-wizarr-instance/api/status
|
||||
```
|
||||
|
||||
## API Endpoints
|
||||
### API Key Management
|
||||
|
||||
### Status
|
||||
1. Log into your Wizarr web interface
|
||||
2. Navigate to **Settings → API Keys**
|
||||
3. Click **"Create API Key"**
|
||||
4. Copy the generated key (shown only once)
|
||||
|
||||
Get overall statistics about your Wizarr instance.
|
||||
### Available Endpoints
|
||||
|
||||
#### GET `/api/status`
|
||||
The API is organized into the following sections:
|
||||
|
||||
Returns basic statistics about users and invitations.
|
||||
- **Status**: System statistics
|
||||
- **Users**: User management across media servers
|
||||
- **Invitations**: Invitation creation and management
|
||||
- **Libraries**: Media library information
|
||||
- **Servers**: Configured media server details
|
||||
- **API Keys**: API key management
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"users": 15,
|
||||
"invites": 8,
|
||||
"pending": 3,
|
||||
"expired": 2
|
||||
}
|
||||
```
|
||||
|
||||
**Example:**
|
||||
```bash
|
||||
curl -H "X-API-Key: your_api_key" \
|
||||
https://your-wizarr-instance.com/api/status
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Users
|
||||
|
||||
Manage users across all configured media servers.
|
||||
|
||||
#### GET `/api/users`
|
||||
|
||||
List all users across all media servers.
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"users": [
|
||||
{
|
||||
"id": 1,
|
||||
"username": "john_doe",
|
||||
"email": "john@example.com",
|
||||
"server": "Main Plex Server",
|
||||
"server_type": "plex",
|
||||
"expires": "2024-08-28T12:00:00Z",
|
||||
"created": "2024-07-28T12:00:00Z"
|
||||
}
|
||||
],
|
||||
"count": 1
|
||||
}
|
||||
```
|
||||
|
||||
**Example:**
|
||||
```bash
|
||||
curl -H "X-API-Key: your_api_key" \
|
||||
https://your-wizarr-instance.com/api/users
|
||||
```
|
||||
|
||||
#### DELETE `/api/users/{user_id}`
|
||||
|
||||
Delete a specific user by ID.
|
||||
|
||||
**Parameters:**
|
||||
- `user_id` (integer) - The ID of the user to delete
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"message": "User john_doe deleted successfully"
|
||||
}
|
||||
```
|
||||
|
||||
**Example:**
|
||||
```bash
|
||||
curl -X DELETE \
|
||||
-H "X-API-Key: your_api_key" \
|
||||
https://your-wizarr-instance.com/api/users/1
|
||||
```
|
||||
|
||||
#### POST `/api/users/{user_id}/extend`
|
||||
|
||||
Extend a user's expiry date.
|
||||
|
||||
**Parameters:**
|
||||
- `user_id` (integer) - The ID of the user
|
||||
- `days` (integer, optional) - Number of days to extend (default: 30)
|
||||
|
||||
**Request Body:**
|
||||
```json
|
||||
{
|
||||
"days": 14
|
||||
}
|
||||
```
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"message": "User john_doe expiry extended by 14 days",
|
||||
"new_expiry": "2024-09-11T12:00:00Z"
|
||||
}
|
||||
```
|
||||
|
||||
**Example:**
|
||||
```bash
|
||||
curl -X POST \
|
||||
-H "X-API-Key: your_api_key" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"days": 14}' \
|
||||
https://your-wizarr-instance.com/api/users/1/extend
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Invitations
|
||||
|
||||
Manage invitation codes for new users.
|
||||
|
||||
#### GET `/api/invitations`
|
||||
|
||||
List all invitations with their current status and server information.
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"invitations": [
|
||||
{
|
||||
"id": 1,
|
||||
"code": "ABC123",
|
||||
"url": "https://your-wizarr-instance.com/j/ABC123",
|
||||
"status": "pending",
|
||||
"created": "2024-07-28T12:00:00Z",
|
||||
"expires": "2024-08-04T12:00:00Z",
|
||||
"used_at": null,
|
||||
"used_by": null,
|
||||
"duration": "30",
|
||||
"unlimited": false,
|
||||
"specific_libraries": null,
|
||||
"display_name": "My Jellyfin Server",
|
||||
"server_names": ["My Jellyfin Server"],
|
||||
"uses_global_setting": false
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"code": "DEF456",
|
||||
"url": "https://your-wizarr-instance.com/j/DEF456",
|
||||
"status": "pending",
|
||||
"created": "2024-07-28T13:00:00Z",
|
||||
"expires": "2024-08-04T13:00:00Z",
|
||||
"used_at": null,
|
||||
"used_by": null,
|
||||
"duration": "unlimited",
|
||||
"unlimited": true,
|
||||
"specific_libraries": null,
|
||||
"display_name": "Plex Server, Jellyfin Server",
|
||||
"server_names": ["Plex Server", "Jellyfin Server"],
|
||||
"uses_global_setting": false
|
||||
}
|
||||
],
|
||||
"count": 2
|
||||
}
|
||||
```
|
||||
|
||||
**Response Fields:**
|
||||
- `url` (string) - Ready-to-use invitation URL that users can click
|
||||
- `display_name` (string) - The resolved display name for the invitation (either global setting or server names)
|
||||
- `server_names` (array) - List of individual server names associated with the invitation
|
||||
- `uses_global_setting` (boolean) - Whether the display name comes from global setting or server names
|
||||
|
||||
**Status Values:**
|
||||
- `pending` - Invitation is active and can be used
|
||||
- `used` - Invitation has been used
|
||||
- `expired` - Invitation has expired
|
||||
|
||||
**Example:**
|
||||
```bash
|
||||
curl -H "X-API-Key: your_api_key" \
|
||||
https://your-wizarr-instance.com/api/invitations
|
||||
```
|
||||
|
||||
#### POST `/api/invitations`
|
||||
|
||||
Create a new invitation.
|
||||
|
||||
**Request Body:**
|
||||
```json
|
||||
{
|
||||
"server_ids": [1, 2],
|
||||
"expires_in_days": 7,
|
||||
"duration": "30",
|
||||
"unlimited": false,
|
||||
"library_ids": [1, 2],
|
||||
"allow_downloads": false,
|
||||
"allow_live_tv": false,
|
||||
"allow_mobile_uploads": false
|
||||
}
|
||||
```
|
||||
|
||||
**Parameters:**
|
||||
- `server_ids` (array, **required**) - Array of server IDs to create invitations for
|
||||
- `expires_in_days` (integer, optional) - Days until invitation expires (1, 7, 30, or null for never)
|
||||
- `duration` (string, optional) - User access duration in days or "unlimited" (default: "unlimited")
|
||||
- `unlimited` (boolean, optional) - Whether user access is unlimited (default: true)
|
||||
- `library_ids` (array, optional) - Array of library IDs to grant access to
|
||||
- `allow_downloads` (boolean, optional) - Allow user downloads (default: false)
|
||||
- `allow_live_tv` (boolean, optional) - Allow live TV access (default: false)
|
||||
- `allow_mobile_uploads` (boolean, optional) - Allow mobile uploads (default: false)
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"message": "Invitation created successfully",
|
||||
"invitation": {
|
||||
"id": 2,
|
||||
"code": "DEF456",
|
||||
"url": "https://your-wizarr-instance.com/j/DEF456",
|
||||
"expires": "2024-08-04T12:00:00Z",
|
||||
"duration": "30",
|
||||
"unlimited": false,
|
||||
"display_name": "My Jellyfin Server",
|
||||
"server_names": ["My Jellyfin Server"],
|
||||
"uses_global_setting": false
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Response Fields:**
|
||||
- `url` (string) - Ready-to-use invitation URL that users can click
|
||||
- `display_name` (string) - The resolved display name for the invitation (either global setting or server names)
|
||||
- `server_names` (array) - List of individual server names associated with the invitation
|
||||
- `uses_global_setting` (boolean) - Whether the display name comes from global setting or server names
|
||||
|
||||
**Error Response (Missing server_ids):**
|
||||
```json
|
||||
{
|
||||
"error": "Server selection is required. Please specify server_ids in request.",
|
||||
"available_servers": [
|
||||
{
|
||||
"id": 1,
|
||||
"name": "Plex Server",
|
||||
"server_type": "plex"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**Example:**
|
||||
```bash
|
||||
curl -X POST \
|
||||
-H "X-API-Key: your_api_key" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"server_ids": [1],
|
||||
"expires_in_days": 7,
|
||||
"duration": "30",
|
||||
"unlimited": false,
|
||||
"library_ids": [1, 2]
|
||||
}' \
|
||||
https://your-wizarr-instance.com/api/invitations
|
||||
```
|
||||
|
||||
#### DELETE `/api/invitations/{invitation_id}`
|
||||
|
||||
Delete a specific invitation.
|
||||
|
||||
**Parameters:**
|
||||
- `invitation_id` (integer) - The ID of the invitation to delete
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"message": "Invitation ABC123 deleted successfully"
|
||||
}
|
||||
```
|
||||
|
||||
**Example:**
|
||||
```bash
|
||||
curl -X DELETE \
|
||||
-H "X-API-Key: your_api_key" \
|
||||
https://your-wizarr-instance.com/api/invitations/1
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Libraries
|
||||
|
||||
Get information about available media libraries.
|
||||
|
||||
#### GET `/api/libraries`
|
||||
|
||||
List all available libraries across all servers.
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"libraries": [
|
||||
{
|
||||
"id": 1,
|
||||
"name": "Movies",
|
||||
"server_id": 1
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"name": "TV Shows",
|
||||
"server_id": 1
|
||||
}
|
||||
],
|
||||
"count": 2
|
||||
}
|
||||
```
|
||||
|
||||
**Example:**
|
||||
```bash
|
||||
curl -H "X-API-Key: your_api_key" \
|
||||
https://your-wizarr-instance.com/api/libraries
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Servers
|
||||
|
||||
Get information about configured media servers.
|
||||
|
||||
#### GET `/api/servers`
|
||||
|
||||
List all configured media servers.
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"servers": [
|
||||
{
|
||||
"id": 1,
|
||||
"name": "Main Plex Server",
|
||||
"server_type": "plex",
|
||||
"server_url": "http://localhost:32400",
|
||||
"external_url": "https://plex.example.com",
|
||||
"verified": true,
|
||||
"allow_downloads": false,
|
||||
"allow_live_tv": true,
|
||||
"allow_mobile_uploads": false,
|
||||
"created_at": "2024-07-28T12:00:00Z"
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"name": "Jellyfin Server",
|
||||
"server_type": "jellyfin",
|
||||
"server_url": "http://localhost:8096",
|
||||
"external_url": null,
|
||||
"verified": true,
|
||||
"allow_downloads": true,
|
||||
"allow_live_tv": false,
|
||||
"allow_mobile_uploads": true,
|
||||
"created_at": "2024-07-28T13:00:00Z"
|
||||
}
|
||||
],
|
||||
"count": 2
|
||||
}
|
||||
```
|
||||
|
||||
**Example:**
|
||||
```bash
|
||||
curl -H "X-API-Key: your_api_key" \
|
||||
https://your-wizarr-instance.com/api/servers
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### API Keys
|
||||
|
||||
Manage API keys programmatically (useful for automation and administrative tasks).
|
||||
|
||||
#### GET `/api/api-keys`
|
||||
|
||||
List all active API keys (excluding the actual key values for security).
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"api_keys": [
|
||||
{
|
||||
"id": 1,
|
||||
"name": "Production API Key",
|
||||
"created_at": "2024-07-28T12:00:00Z",
|
||||
"last_used_at": "2024-07-28T14:30:00Z",
|
||||
"created_by": "admin"
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"name": "Development Key",
|
||||
"created_at": "2024-07-28T13:00:00Z",
|
||||
"last_used_at": null,
|
||||
"created_by": "admin"
|
||||
}
|
||||
],
|
||||
"count": 2
|
||||
}
|
||||
```
|
||||
|
||||
**Example:**
|
||||
```bash
|
||||
curl -H "X-API-Key: your_api_key" \
|
||||
https://your-wizarr-instance.com/api/api-keys
|
||||
```
|
||||
|
||||
#### DELETE `/api/api-keys/{key_id}`
|
||||
|
||||
Delete a specific API key (soft delete - marks as inactive).
|
||||
|
||||
**Parameters:**
|
||||
- `key_id` (integer) - The ID of the API key to delete
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"message": "API key 'Development Key' deleted successfully"
|
||||
}
|
||||
```
|
||||
|
||||
**Security Note:** You cannot delete the API key that is currently being used for the request.
|
||||
|
||||
**Example:**
|
||||
```bash
|
||||
curl -X DELETE \
|
||||
-H "X-API-Key: your_api_key" \
|
||||
https://your-wizarr-instance.com/api/api-keys/2
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Error Handling
|
||||
|
||||
The API uses standard HTTP status codes to indicate success or failure of requests.
|
||||
|
||||
### HTTP Status Codes
|
||||
|
||||
- `200 OK` - Request successful
|
||||
- `201 Created` - Resource created successfully
|
||||
- `400 Bad Request` - Invalid request parameters
|
||||
- `401 Unauthorized` - Invalid or missing API key
|
||||
- `404 Not Found` - Resource not found
|
||||
- `500 Internal Server Error` - Server error
|
||||
|
||||
### Error Response Format
|
||||
|
||||
```json
|
||||
{
|
||||
"error": "Error message describing what went wrong"
|
||||
}
|
||||
```
|
||||
|
||||
### Common Error Examples
|
||||
|
||||
**Missing API Key:**
|
||||
```json
|
||||
{
|
||||
"error": "API key required"
|
||||
}
|
||||
```
|
||||
|
||||
**Invalid API Key:**
|
||||
```json
|
||||
{
|
||||
"error": "Invalid API key"
|
||||
}
|
||||
```
|
||||
|
||||
**Resource Not Found:**
|
||||
```json
|
||||
{
|
||||
"error": "User not found"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Rate Limiting
|
||||
|
||||
Currently, there are no explicit rate limits on the API endpoints. However, it's recommended to make requests responsibly to avoid overwhelming the server.
|
||||
|
||||
---
|
||||
|
||||
## Examples
|
||||
|
||||
### Complete User Management Workflow
|
||||
|
||||
#### 1. Check System Status
|
||||
```bash
|
||||
curl -H "X-API-Key: your_api_key" \
|
||||
https://your-wizarr-instance.com/api/status
|
||||
```
|
||||
|
||||
#### 2. List All Users
|
||||
```bash
|
||||
curl -H "X-API-Key: your_api_key" \
|
||||
https://your-wizarr-instance.com/api/users
|
||||
```
|
||||
|
||||
#### 3. Create an Invitation
|
||||
```bash
|
||||
curl -X POST \
|
||||
-H "X-API-Key: your_api_key" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"expires_in_days": 7,
|
||||
"duration": "30",
|
||||
"unlimited": false
|
||||
}' \
|
||||
https://your-wizarr-instance.com/api/invitations
|
||||
```
|
||||
|
||||
#### 4. Extend User Access
|
||||
```bash
|
||||
curl -X POST \
|
||||
-H "X-API-Key: your_api_key" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"days": 30}' \
|
||||
https://your-wizarr-instance.com/api/users/1/extend
|
||||
```
|
||||
|
||||
### Python Example
|
||||
### Example Usage
|
||||
|
||||
```python
|
||||
import requests
|
||||
|
||||
API_KEY = "your_api_key_here"
|
||||
BASE_URL = "https://your-wizarr-instance.com/api"
|
||||
|
||||
BASE_URL = "http://your-wizarr-instance/api"
|
||||
headers = {"X-API-Key": API_KEY}
|
||||
|
||||
# Get status
|
||||
# Get system status
|
||||
response = requests.get(f"{BASE_URL}/status", headers=headers)
|
||||
status_data = response.json()
|
||||
print(f"Total users: {status_data['users']}")
|
||||
print(response.json())
|
||||
|
||||
# Create invitation
|
||||
invitation_data = {
|
||||
data = {
|
||||
"server_ids": [1],
|
||||
"expires_in_days": 7,
|
||||
"duration": "30",
|
||||
@@ -579,90 +64,22 @@ invitation_data = {
|
||||
}
|
||||
|
||||
response = requests.post(
|
||||
f"{BASE_URL}/invitations",
|
||||
f"{BASE_URL}/invitations",
|
||||
headers={**headers, "Content-Type": "application/json"},
|
||||
json=invitation_data
|
||||
json=data
|
||||
)
|
||||
|
||||
if response.status_code == 201:
|
||||
invitation = response.json()
|
||||
print(f"Created invitation: {invitation['invitation']['code']}")
|
||||
print(f"Invitation URL: {invitation['invitation']['url']}")
|
||||
```
|
||||
|
||||
### JavaScript Example
|
||||
## Interactive Testing
|
||||
|
||||
```javascript
|
||||
const API_KEY = 'your_api_key_here';
|
||||
const BASE_URL = 'https://your-wizarr-instance.com/api';
|
||||
Visit `/api/docs/` on your Wizarr instance to:
|
||||
- Browse all available endpoints
|
||||
- Test API calls directly in your browser
|
||||
- View detailed request/response examples
|
||||
- Download the OpenAPI specification
|
||||
|
||||
const headers = {
|
||||
'X-API-Key': API_KEY,
|
||||
'Content-Type': 'application/json'
|
||||
};
|
||||
|
||||
// Get all users
|
||||
fetch(`${BASE_URL}/users`, { headers })
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
console.log(`Found ${data.count} users`);
|
||||
data.users.forEach(user => {
|
||||
console.log(`- ${user.username} (${user.server})`);
|
||||
});
|
||||
});
|
||||
|
||||
// Create invitation
|
||||
const invitationData = {
|
||||
server_ids: [1],
|
||||
expires_in_days: 7,
|
||||
duration: "30",
|
||||
unlimited: false
|
||||
};
|
||||
|
||||
fetch(`${BASE_URL}/invitations`, {
|
||||
method: 'POST',
|
||||
headers,
|
||||
body: JSON.stringify(invitationData)
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.invitation) {
|
||||
console.log(`Created invitation: ${data.invitation.code}`);
|
||||
console.log(`Invitation URL: ${data.invitation.url}`);
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## API Key Management
|
||||
|
||||
### Creating API Keys
|
||||
|
||||
1. Log into your Wizarr web interface
|
||||
2. Navigate to Settings → API Keys
|
||||
3. Click "Create API Key"
|
||||
4. Enter a descriptive name
|
||||
5. Copy the generated API key (it will only be shown once)
|
||||
|
||||
### Security Best Practices
|
||||
|
||||
- Store API keys securely and never commit them to version control
|
||||
- Use different API keys for different applications or environments
|
||||
- Rotate API keys regularly
|
||||
- Delete unused API keys
|
||||
- Monitor API key usage through the web interface
|
||||
|
||||
### API Key Permissions
|
||||
|
||||
Currently, all API keys have full access to all endpoints. Future versions may include granular permissions.
|
||||
|
||||
---
|
||||
|
||||
## Changelog
|
||||
|
||||
- **v2.2.1** - Added initial API documentation
|
||||
- Added comprehensive endpoint documentation
|
||||
- Fixed API key deletion UI glitch
|
||||
- Improved error handling and response formats
|
||||
- Added comprehensive test coverage
|
||||
The Swagger UI provides the most up-to-date and complete API documentation.
|
||||
@@ -15,6 +15,7 @@ dependencies = [
|
||||
"flask-limiter>=3.8.0",
|
||||
"flask-login>=0.6.3",
|
||||
"flask-migrate>=4.1.0",
|
||||
"flask-restx>=1.3.0",
|
||||
"flask-session>=0.8.0",
|
||||
"flask-sqlalchemy>=3.1.1",
|
||||
"flask-wtf>=1.2.2",
|
||||
|
||||
152
uv.lock
generated
152
uv.lock
generated
@@ -16,6 +16,15 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/41/18/d89a443ed1ab9bcda16264716f809c663866d4ca8de218aa78fd50b38ead/alembic-1.15.2-py3-none-any.whl", hash = "sha256:2e76bd916d547f6900ec4bb5a90aeac1485d2c92536923d0b138c02b126edc53", size = 231911, upload-time = "2025-03-28T13:52:02.218Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aniso8601"
|
||||
version = "10.0.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/8b/8d/52179c4e3f1978d3d9a285f98c706642522750ef343e9738286130423730/aniso8601-10.0.1.tar.gz", hash = "sha256:25488f8663dd1528ae1f54f94ac1ea51ae25b4d531539b8bc707fed184d16845", size = 47190 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/59/75/e0e10dc7ed1408c28e03a6cb2d7a407f99320eb953f229d008a7a6d05546/aniso8601-10.0.1-py2.py3-none-any.whl", hash = "sha256:eb19717fd4e0db6de1aab06f12450ab92144246b257423fe020af5748c0cb89e", size = 52848 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "apprise"
|
||||
version = "1.9.4"
|
||||
@@ -54,6 +63,15 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/c9/7f/09065fd9e27da0eda08b4d6897f1c13535066174cc023af248fc2a8d5e5a/asn1crypto-1.5.1-py2.py3-none-any.whl", hash = "sha256:db4e40728b728508912cbb3d44f19ce188f218e9eba635821bb4b68564f8fd67", size = 105045, upload-time = "2022-03-15T14:46:51.055Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "attrs"
|
||||
version = "25.3.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/5a/b0/1367933a8532ee6ff8d63537de4f1177af4bff9f3e829baf7331f595bb24/attrs-25.3.0.tar.gz", hash = "sha256:75d7cefc7fb576747b2c81b4442d4d4a1ce0900973527c011d1030fd3bf4af1b", size = 812032 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/77/06/bb80f5f86020c4551da315d78b3ab75e8228f89f0162f2c3a819e407941a/attrs-25.3.0-py3-none-any.whl", hash = "sha256:427318ce031701fea540783410126f03899a97ffc6f61596ad581ac2e40e3bc3", size = 63815 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "babel"
|
||||
version = "2.17.0"
|
||||
@@ -373,6 +391,23 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/d2/c4/3f329b23d769fe7628a5fc57ad36956f1fb7132cf8837be6da762b197327/Flask_Migrate-4.1.0-py3-none-any.whl", hash = "sha256:24d8051af161782e0743af1b04a152d007bad9772b2bca67b7ec1e8ceeb3910d", size = 21237, upload-time = "2025-01-10T18:51:09.527Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "flask-restx"
|
||||
version = "1.3.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "aniso8601" },
|
||||
{ name = "flask" },
|
||||
{ name = "importlib-resources" },
|
||||
{ name = "jsonschema" },
|
||||
{ name = "pytz" },
|
||||
{ name = "werkzeug" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/45/4c/2e7d84e2b406b47cf3bf730f521efe474977b404ee170d8ea68dc37e6733/flask-restx-1.3.0.tar.gz", hash = "sha256:4f3d3fa7b6191fcc715b18c201a12cd875176f92ba4acc61626ccfd571ee1728", size = 2814072 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/a5/bf/1907369f2a7ee614dde5152ff8f811159d357e77962aa3f8c2e937f63731/flask_restx-1.3.0-py2.py3-none-any.whl", hash = "sha256:636c56c3fb3f2c1df979e748019f084a938c4da2035a3e535a4673e4fc177691", size = 2798683 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "flask-session"
|
||||
version = "0.8.0"
|
||||
@@ -469,6 +504,15 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442, upload-time = "2024-09-15T18:07:37.964Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "importlib-resources"
|
||||
version = "6.5.2"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/cf/8c/f834fbf984f691b4f7ff60f50b514cc3de5cc08abfc3295564dd89c5e2e7/importlib_resources-6.5.2.tar.gz", hash = "sha256:185f87adef5bcc288449d98fb4fba07cea78bc036455dd44c5fc4a2fe78fed2c", size = 44693 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/a4/ed/1f1afb2e9e7f38a545d628f864d562a5ae64fe6f7a10e28ffb9b185b4e89/importlib_resources-6.5.2-py3-none-any.whl", hash = "sha256:789cfdc3ed28c78b67a06acb8126751ced69a3d5f79c095a98298cd8a760ccec", size = 37461 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "iniconfig"
|
||||
version = "2.1.0"
|
||||
@@ -499,6 +543,33 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jsonschema"
|
||||
version = "4.25.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "attrs" },
|
||||
{ name = "jsonschema-specifications" },
|
||||
{ name = "referencing" },
|
||||
{ name = "rpds-py" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/d5/00/a297a868e9d0784450faa7365c2172a7d6110c763e30ba861867c32ae6a9/jsonschema-4.25.0.tar.gz", hash = "sha256:e63acf5c11762c0e6672ffb61482bdf57f0876684d8d249c0fe2d730d48bc55f", size = 356830 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/fe/54/c86cd8e011fe98803d7e382fd67c0df5ceab8d2b7ad8c5a81524f791551c/jsonschema-4.25.0-py3-none-any.whl", hash = "sha256:24c2e8da302de79c8b9382fee3e76b355e44d2a4364bb207159ce10b517bd716", size = 89184 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jsonschema-specifications"
|
||||
version = "2025.4.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "referencing" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/bf/ce/46fbd9c8119cfc3581ee5643ea49464d168028cfb5caff5fc0596d0cf914/jsonschema_specifications-2025.4.1.tar.gz", hash = "sha256:630159c9f4dbea161a6a2205c3011cc4f18ff381b189fff48bb39b9bf26ae608", size = 15513 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/01/0e/b27cdbaccf30b890c40ed1da9fd4a3593a5cf94dae54fb34f8a4b74fcd3f/jsonschema_specifications-2025.4.1-py3-none-any.whl", hash = "sha256:4653bffbd6584f7de83a67e0d620ef16900b390ddc7939d56684d6c81e33f1af", size = 18437 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "limits"
|
||||
version = "5.4.0"
|
||||
@@ -824,6 +895,19 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/fa/de/02b54f42487e3d3c6efb3f89428677074ca7bf43aae402517bc7cca949f3/PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563", size = 156446, upload-time = "2024-08-06T20:33:04.33Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "referencing"
|
||||
version = "0.36.2"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "attrs" },
|
||||
{ name = "rpds-py" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/2f/db/98b5c277be99dd18bfd91dd04e1b759cad18d1a338188c936e92f921c7e2/referencing-0.36.2.tar.gz", hash = "sha256:df2e89862cd09deabbdba16944cc3f10feb6b3e6f18e902f7cc25609a34775aa", size = 74744 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/c1/b1/3baf80dc6d2b7bc27a95a67752d0208e410351e3feb4eb78de5f77454d8d/referencing-0.36.2-py3-none-any.whl", hash = "sha256:e8699adbbf8b5c7de96d8ffa0eb5c158b3beafce084968e2ea8bb08c6794dcd0", size = 26775 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "requests"
|
||||
version = "2.32.4"
|
||||
@@ -865,6 +949,72 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/19/71/39c7c0d87f8d4e6c020a393182060eaefeeae6c01dab6a84ec346f2567df/rich-13.9.4-py3-none-any.whl", hash = "sha256:6049d5e6ec054bf2779ab3358186963bac2ea89175919d699e378b99738c2a90", size = 242424, upload-time = "2024-11-01T16:43:55.817Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rpds-py"
|
||||
version = "0.27.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/1e/d9/991a0dee12d9fc53ed027e26a26a64b151d77252ac477e22666b9688bc16/rpds_py-0.27.0.tar.gz", hash = "sha256:8b23cf252f180cda89220b378d917180f29d313cd6a07b2431c0d3b776aae86f", size = 27420 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/81/d2/dfdfd42565a923b9e5a29f93501664f5b984a802967d48d49200ad71be36/rpds_py-0.27.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:443d239d02d9ae55b74015234f2cd8eb09e59fbba30bf60baeb3123ad4c6d5ff", size = 362133 },
|
||||
{ url = "https://files.pythonhosted.org/packages/ac/4a/0a2e2460c4b66021d349ce9f6331df1d6c75d7eea90df9785d333a49df04/rpds_py-0.27.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:b8a7acf04fda1f30f1007f3cc96d29d8cf0a53e626e4e1655fdf4eabc082d367", size = 347128 },
|
||||
{ url = "https://files.pythonhosted.org/packages/35/8d/7d1e4390dfe09d4213b3175a3f5a817514355cb3524593380733204f20b9/rpds_py-0.27.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9d0f92b78cfc3b74a42239fdd8c1266f4715b573204c234d2f9fc3fc7a24f185", size = 384027 },
|
||||
{ url = "https://files.pythonhosted.org/packages/c1/65/78499d1a62172891c8cd45de737b2a4b84a414b6ad8315ab3ac4945a5b61/rpds_py-0.27.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ce4ed8e0c7dbc5b19352b9c2c6131dd23b95fa8698b5cdd076307a33626b72dc", size = 399973 },
|
||||
{ url = "https://files.pythonhosted.org/packages/10/a1/1c67c1d8cc889107b19570bb01f75cf49852068e95e6aee80d22915406fc/rpds_py-0.27.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fde355b02934cc6b07200cc3b27ab0c15870a757d1a72fd401aa92e2ea3c6bfe", size = 515295 },
|
||||
{ url = "https://files.pythonhosted.org/packages/df/27/700ec88e748436b6c7c4a2262d66e80f8c21ab585d5e98c45e02f13f21c0/rpds_py-0.27.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:13bbc4846ae4c993f07c93feb21a24d8ec637573d567a924b1001e81c8ae80f9", size = 406737 },
|
||||
{ url = "https://files.pythonhosted.org/packages/33/cc/6b0ee8f0ba3f2df2daac1beda17fde5cf10897a7d466f252bd184ef20162/rpds_py-0.27.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:be0744661afbc4099fef7f4e604e7f1ea1be1dd7284f357924af12a705cc7d5c", size = 385898 },
|
||||
{ url = "https://files.pythonhosted.org/packages/e8/7e/c927b37d7d33c0a0ebf249cc268dc2fcec52864c1b6309ecb960497f2285/rpds_py-0.27.0-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:069e0384a54f427bd65d7fda83b68a90606a3835901aaff42185fcd94f5a9295", size = 405785 },
|
||||
{ url = "https://files.pythonhosted.org/packages/5b/d2/8ed50746d909dcf402af3fa58b83d5a590ed43e07251d6b08fad1a535ba6/rpds_py-0.27.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4bc262ace5a1a7dc3e2eac2fa97b8257ae795389f688b5adf22c5db1e2431c43", size = 419760 },
|
||||
{ url = "https://files.pythonhosted.org/packages/d3/60/2b2071aee781cb3bd49f94d5d35686990b925e9b9f3e3d149235a6f5d5c1/rpds_py-0.27.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:2fe6e18e5c8581f0361b35ae575043c7029d0a92cb3429e6e596c2cdde251432", size = 561201 },
|
||||
{ url = "https://files.pythonhosted.org/packages/98/1f/27b67304272521aaea02be293fecedce13fa351a4e41cdb9290576fc6d81/rpds_py-0.27.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d93ebdb82363d2e7bec64eecdc3632b59e84bd270d74fe5be1659f7787052f9b", size = 591021 },
|
||||
{ url = "https://files.pythonhosted.org/packages/db/9b/a2fadf823164dd085b1f894be6443b0762a54a7af6f36e98e8fcda69ee50/rpds_py-0.27.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:0954e3a92e1d62e83a54ea7b3fdc9efa5d61acef8488a8a3d31fdafbfb00460d", size = 556368 },
|
||||
{ url = "https://files.pythonhosted.org/packages/24/f3/6d135d46a129cda2e3e6d4c5e91e2cc26ea0428c6cf152763f3f10b6dd05/rpds_py-0.27.0-cp313-cp313-win32.whl", hash = "sha256:2cff9bdd6c7b906cc562a505c04a57d92e82d37200027e8d362518df427f96cd", size = 221236 },
|
||||
{ url = "https://files.pythonhosted.org/packages/c5/44/65d7494f5448ecc755b545d78b188440f81da98b50ea0447ab5ebfdf9bd6/rpds_py-0.27.0-cp313-cp313-win_amd64.whl", hash = "sha256:dc79d192fb76fc0c84f2c58672c17bbbc383fd26c3cdc29daae16ce3d927e8b2", size = 232634 },
|
||||
{ url = "https://files.pythonhosted.org/packages/70/d9/23852410fadab2abb611733933401de42a1964ce6600a3badae35fbd573e/rpds_py-0.27.0-cp313-cp313-win_arm64.whl", hash = "sha256:5b3a5c8089eed498a3af23ce87a80805ff98f6ef8f7bdb70bd1b7dae5105f6ac", size = 222783 },
|
||||
{ url = "https://files.pythonhosted.org/packages/15/75/03447917f78512b34463f4ef11066516067099a0c466545655503bed0c77/rpds_py-0.27.0-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:90fb790138c1a89a2e58c9282fe1089638401f2f3b8dddd758499041bc6e0774", size = 359154 },
|
||||
{ url = "https://files.pythonhosted.org/packages/6b/fc/4dac4fa756451f2122ddaf136e2c6aeb758dc6fdbe9ccc4bc95c98451d50/rpds_py-0.27.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:010c4843a3b92b54373e3d2291a7447d6c3fc29f591772cc2ea0e9f5c1da434b", size = 343909 },
|
||||
{ url = "https://files.pythonhosted.org/packages/7b/81/723c1ed8e6f57ed9d8c0c07578747a2d3d554aaefc1ab89f4e42cfeefa07/rpds_py-0.27.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c9ce7a9e967afc0a2af7caa0d15a3e9c1054815f73d6a8cb9225b61921b419bd", size = 379340 },
|
||||
{ url = "https://files.pythonhosted.org/packages/98/16/7e3740413de71818ce1997df82ba5f94bae9fff90c0a578c0e24658e6201/rpds_py-0.27.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:aa0bf113d15e8abdfee92aa4db86761b709a09954083afcb5bf0f952d6065fdb", size = 391655 },
|
||||
{ url = "https://files.pythonhosted.org/packages/e0/63/2a9f510e124d80660f60ecce07953f3f2d5f0b96192c1365443859b9c87f/rpds_py-0.27.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:eb91d252b35004a84670dfeafadb042528b19842a0080d8b53e5ec1128e8f433", size = 513017 },
|
||||
{ url = "https://files.pythonhosted.org/packages/2c/4e/cf6ff311d09776c53ea1b4f2e6700b9d43bb4e99551006817ade4bbd6f78/rpds_py-0.27.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:db8a6313dbac934193fc17fe7610f70cd8181c542a91382531bef5ed785e5615", size = 402058 },
|
||||
{ url = "https://files.pythonhosted.org/packages/88/11/5e36096d474cb10f2a2d68b22af60a3bc4164fd8db15078769a568d9d3ac/rpds_py-0.27.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce96ab0bdfcef1b8c371ada2100767ace6804ea35aacce0aef3aeb4f3f499ca8", size = 383474 },
|
||||
{ url = "https://files.pythonhosted.org/packages/db/a2/3dff02805b06058760b5eaa6d8cb8db3eb3e46c9e452453ad5fc5b5ad9fe/rpds_py-0.27.0-cp313-cp313t-manylinux_2_31_riscv64.whl", hash = "sha256:7451ede3560086abe1aa27dcdcf55cd15c96b56f543fb12e5826eee6f721f858", size = 400067 },
|
||||
{ url = "https://files.pythonhosted.org/packages/67/87/eed7369b0b265518e21ea836456a4ed4a6744c8c12422ce05bce760bb3cf/rpds_py-0.27.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:32196b5a99821476537b3f7732432d64d93a58d680a52c5e12a190ee0135d8b5", size = 412085 },
|
||||
{ url = "https://files.pythonhosted.org/packages/8b/48/f50b2ab2fbb422fbb389fe296e70b7a6b5ea31b263ada5c61377e710a924/rpds_py-0.27.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:a029be818059870664157194e46ce0e995082ac49926f1423c1f058534d2aaa9", size = 555928 },
|
||||
{ url = "https://files.pythonhosted.org/packages/98/41/b18eb51045d06887666c3560cd4bbb6819127b43d758f5adb82b5f56f7d1/rpds_py-0.27.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3841f66c1ffdc6cebce8aed64e36db71466f1dc23c0d9a5592e2a782a3042c79", size = 585527 },
|
||||
{ url = "https://files.pythonhosted.org/packages/be/03/a3dd6470fc76499959b00ae56295b76b4bdf7c6ffc60d62006b1217567e1/rpds_py-0.27.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:42894616da0fc0dcb2ec08a77896c3f56e9cb2f4b66acd76fc8992c3557ceb1c", size = 554211 },
|
||||
{ url = "https://files.pythonhosted.org/packages/bf/d1/ee5fd1be395a07423ac4ca0bcc05280bf95db2b155d03adefeb47d5ebf7e/rpds_py-0.27.0-cp313-cp313t-win32.whl", hash = "sha256:b1fef1f13c842a39a03409e30ca0bf87b39a1e2a305a9924deadb75a43105d23", size = 216624 },
|
||||
{ url = "https://files.pythonhosted.org/packages/1c/94/4814c4c858833bf46706f87349c37ca45e154da7dbbec9ff09f1abeb08cc/rpds_py-0.27.0-cp313-cp313t-win_amd64.whl", hash = "sha256:183f5e221ba3e283cd36fdfbe311d95cd87699a083330b4f792543987167eff1", size = 230007 },
|
||||
{ url = "https://files.pythonhosted.org/packages/0e/a5/8fffe1c7dc7c055aa02df310f9fb71cfc693a4d5ccc5de2d3456ea5fb022/rpds_py-0.27.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:f3cd110e02c5bf17d8fb562f6c9df5c20e73029d587cf8602a2da6c5ef1e32cb", size = 362595 },
|
||||
{ url = "https://files.pythonhosted.org/packages/bc/c7/4e4253fd2d4bb0edbc0b0b10d9f280612ca4f0f990e3c04c599000fe7d71/rpds_py-0.27.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:8d0e09cf4863c74106b5265c2c310f36146e2b445ff7b3018a56799f28f39f6f", size = 347252 },
|
||||
{ url = "https://files.pythonhosted.org/packages/f3/c8/3d1a954d30f0174dd6baf18b57c215da03cf7846a9d6e0143304e784cddc/rpds_py-0.27.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:64f689ab822f9b5eb6dfc69893b4b9366db1d2420f7db1f6a2adf2a9ca15ad64", size = 384886 },
|
||||
{ url = "https://files.pythonhosted.org/packages/e0/52/3c5835f2df389832b28f9276dd5395b5a965cea34226e7c88c8fbec2093c/rpds_py-0.27.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e36c80c49853b3ffda7aa1831bf175c13356b210c73128c861f3aa93c3cc4015", size = 399716 },
|
||||
{ url = "https://files.pythonhosted.org/packages/40/73/176e46992461a1749686a2a441e24df51ff86b99c2d34bf39f2a5273b987/rpds_py-0.27.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6de6a7f622860af0146cb9ee148682ff4d0cea0b8fd3ad51ce4d40efb2f061d0", size = 517030 },
|
||||
{ url = "https://files.pythonhosted.org/packages/79/2a/7266c75840e8c6e70effeb0d38922a45720904f2cd695e68a0150e5407e2/rpds_py-0.27.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4045e2fc4b37ec4b48e8907a5819bdd3380708c139d7cc358f03a3653abedb89", size = 408448 },
|
||||
{ url = "https://files.pythonhosted.org/packages/e6/5f/a7efc572b8e235093dc6cf39f4dbc8a7f08e65fdbcec7ff4daeb3585eef1/rpds_py-0.27.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9da162b718b12c4219eeeeb68a5b7552fbc7aadedf2efee440f88b9c0e54b45d", size = 387320 },
|
||||
{ url = "https://files.pythonhosted.org/packages/a2/eb/9ff6bc92efe57cf5a2cb74dee20453ba444b6fdc85275d8c99e0d27239d1/rpds_py-0.27.0-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:0665be515767dc727ffa5f74bd2ef60b0ff85dad6bb8f50d91eaa6b5fb226f51", size = 407414 },
|
||||
{ url = "https://files.pythonhosted.org/packages/fb/bd/3b9b19b00d5c6e1bd0f418c229ab0f8d3b110ddf7ec5d9d689ef783d0268/rpds_py-0.27.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:203f581accef67300a942e49a37d74c12ceeef4514874c7cede21b012613ca2c", size = 420766 },
|
||||
{ url = "https://files.pythonhosted.org/packages/17/6b/521a7b1079ce16258c70805166e3ac6ec4ee2139d023fe07954dc9b2d568/rpds_py-0.27.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7873b65686a6471c0037139aa000d23fe94628e0daaa27b6e40607c90e3f5ec4", size = 562409 },
|
||||
{ url = "https://files.pythonhosted.org/packages/8b/bf/65db5bfb14ccc55e39de8419a659d05a2a9cd232f0a699a516bb0991da7b/rpds_py-0.27.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:249ab91ceaa6b41abc5f19513cb95b45c6f956f6b89f1fe3d99c81255a849f9e", size = 590793 },
|
||||
{ url = "https://files.pythonhosted.org/packages/db/b8/82d368b378325191ba7aae8f40f009b78057b598d4394d1f2cdabaf67b3f/rpds_py-0.27.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d2f184336bc1d6abfaaa1262ed42739c3789b1e3a65a29916a615307d22ffd2e", size = 558178 },
|
||||
{ url = "https://files.pythonhosted.org/packages/f6/ff/f270bddbfbc3812500f8131b1ebbd97afd014cd554b604a3f73f03133a36/rpds_py-0.27.0-cp314-cp314-win32.whl", hash = "sha256:d3c622c39f04d5751408f5b801ecb527e6e0a471b367f420a877f7a660d583f6", size = 222355 },
|
||||
{ url = "https://files.pythonhosted.org/packages/bf/20/fdab055b1460c02ed356a0e0b0a78c1dd32dc64e82a544f7b31c9ac643dc/rpds_py-0.27.0-cp314-cp314-win_amd64.whl", hash = "sha256:cf824aceaeffff029ccfba0da637d432ca71ab21f13e7f6f5179cd88ebc77a8a", size = 234007 },
|
||||
{ url = "https://files.pythonhosted.org/packages/4d/a8/694c060005421797a3be4943dab8347c76c2b429a9bef68fb2c87c9e70c7/rpds_py-0.27.0-cp314-cp314-win_arm64.whl", hash = "sha256:86aca1616922b40d8ac1b3073a1ead4255a2f13405e5700c01f7c8d29a03972d", size = 223527 },
|
||||
{ url = "https://files.pythonhosted.org/packages/1e/f9/77f4c90f79d2c5ca8ce6ec6a76cb4734ee247de6b3a4f337e289e1f00372/rpds_py-0.27.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:341d8acb6724c0c17bdf714319c393bb27f6d23d39bc74f94221b3e59fc31828", size = 359469 },
|
||||
{ url = "https://files.pythonhosted.org/packages/c0/22/b97878d2f1284286fef4172069e84b0b42b546ea7d053e5fb7adb9ac6494/rpds_py-0.27.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:6b96b0b784fe5fd03beffff2b1533dc0d85e92bab8d1b2c24ef3a5dc8fac5669", size = 343960 },
|
||||
{ url = "https://files.pythonhosted.org/packages/b1/b0/dfd55b5bb480eda0578ae94ef256d3061d20b19a0f5e18c482f03e65464f/rpds_py-0.27.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0c431bfb91478d7cbe368d0a699978050d3b112d7f1d440a41e90faa325557fd", size = 380201 },
|
||||
{ url = "https://files.pythonhosted.org/packages/28/22/e1fa64e50d58ad2b2053077e3ec81a979147c43428de9e6de68ddf6aff4e/rpds_py-0.27.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:20e222a44ae9f507d0f2678ee3dd0c45ec1e930f6875d99b8459631c24058aec", size = 392111 },
|
||||
{ url = "https://files.pythonhosted.org/packages/49/f9/43ab7a43e97aedf6cea6af70fdcbe18abbbc41d4ae6cdec1bfc23bbad403/rpds_py-0.27.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:184f0d7b342967f6cda94a07d0e1fae177d11d0b8f17d73e06e36ac02889f303", size = 515863 },
|
||||
{ url = "https://files.pythonhosted.org/packages/38/9b/9bd59dcc636cd04d86a2d20ad967770bf348f5eb5922a8f29b547c074243/rpds_py-0.27.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a00c91104c173c9043bc46f7b30ee5e6d2f6b1149f11f545580f5d6fdff42c0b", size = 402398 },
|
||||
{ url = "https://files.pythonhosted.org/packages/71/bf/f099328c6c85667aba6b66fa5c35a8882db06dcd462ea214be72813a0dd2/rpds_py-0.27.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f7a37dd208f0d658e0487522078b1ed68cd6bce20ef4b5a915d2809b9094b410", size = 384665 },
|
||||
{ url = "https://files.pythonhosted.org/packages/a9/c5/9c1f03121ece6634818490bd3c8be2c82a70928a19de03467fb25a3ae2a8/rpds_py-0.27.0-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:92f3b3ec3e6008a1fe00b7c0946a170f161ac00645cde35e3c9a68c2475e8156", size = 400405 },
|
||||
{ url = "https://files.pythonhosted.org/packages/b5/b8/e25d54af3e63ac94f0c16d8fe143779fe71ff209445a0c00d0f6984b6b2c/rpds_py-0.27.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a1b3db5fae5cbce2131b7420a3f83553d4d89514c03d67804ced36161fe8b6b2", size = 413179 },
|
||||
{ url = "https://files.pythonhosted.org/packages/f9/d1/406b3316433fe49c3021546293a04bc33f1478e3ec7950215a7fce1a1208/rpds_py-0.27.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:5355527adaa713ab693cbce7c1e0ec71682f599f61b128cf19d07e5c13c9b1f1", size = 556895 },
|
||||
{ url = "https://files.pythonhosted.org/packages/5f/bc/3697c0c21fcb9a54d46ae3b735eb2365eea0c2be076b8f770f98e07998de/rpds_py-0.27.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:fcc01c57ce6e70b728af02b2401c5bc853a9e14eb07deda30624374f0aebfe42", size = 585464 },
|
||||
{ url = "https://files.pythonhosted.org/packages/63/09/ee1bb5536f99f42c839b177d552f6114aa3142d82f49cef49261ed28dbe0/rpds_py-0.27.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:3001013dae10f806380ba739d40dee11db1ecb91684febb8406a87c2ded23dae", size = 555090 },
|
||||
{ url = "https://files.pythonhosted.org/packages/7d/2c/363eada9e89f7059199d3724135a86c47082cbf72790d6ba2f336d146ddb/rpds_py-0.27.0-cp314-cp314t-win32.whl", hash = "sha256:0f401c369186a5743694dd9fc08cba66cf70908757552e1f714bfc5219c655b5", size = 218001 },
|
||||
{ url = "https://files.pythonhosted.org/packages/e2/3f/d6c216ed5199c9ef79e2a33955601f454ed1e7420a93b89670133bca5ace/rpds_py-0.27.0-cp314-cp314t-win_amd64.whl", hash = "sha256:8a1dca5507fa1337f75dcd5070218b20bc68cf8844271c923c1b79dfcbc20391", size = 230993 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ruff"
|
||||
version = "0.12.8"
|
||||
@@ -1049,6 +1199,7 @@ dependencies = [
|
||||
{ name = "flask-limiter" },
|
||||
{ name = "flask-login" },
|
||||
{ name = "flask-migrate" },
|
||||
{ name = "flask-restx" },
|
||||
{ name = "flask-session" },
|
||||
{ name = "flask-sqlalchemy" },
|
||||
{ name = "flask-wtf" },
|
||||
@@ -1089,6 +1240,7 @@ requires-dist = [
|
||||
{ name = "flask-limiter", specifier = ">=3.8.0" },
|
||||
{ name = "flask-login", specifier = ">=0.6.3" },
|
||||
{ name = "flask-migrate", specifier = ">=4.1.0" },
|
||||
{ name = "flask-restx", specifier = ">=1.3.0" },
|
||||
{ name = "flask-session", specifier = ">=0.8.0" },
|
||||
{ name = "flask-sqlalchemy", specifier = ">=3.1.1" },
|
||||
{ name = "flask-wtf", specifier = ">=1.2.2" },
|
||||
|
||||
Reference in New Issue
Block a user