diff --git a/app/Config/OpenApi.php b/app/Config/OpenApi.php new file mode 100644 index 0000000..f74e8ce --- /dev/null +++ b/app/Config/OpenApi.php @@ -0,0 +1,101 @@ +userModel = new UserModel(); @@ -189,4 +191,175 @@ public function logout(): \CodeIgniter\HTTP\RedirectResponse return $this->handleException($e); } } + + // API Methods + + /** + * API Login endpoint + */ + #[OA\Post( + path: "/auth/login", + summary: "User login", + description: "Authenticate a user and return a JWT token", + requestBody: new OA\RequestBody( + required: true, + content: new OA\JsonContent( + required: ["email", "password"], + properties: [ + new OA\Property(property: "email", type: "string", format: "email", example: "user@example.com"), + new OA\Property(property: "password", type: "string", format: "password", example: "password123") + ] + ) + ), + tags: ["Authentication"], + responses: [ + new OA\Response( + response: 200, + description: "Login successful", + content: new OA\JsonContent( + properties: [ + new OA\Property(property: "success", type: "boolean", example: true), + new OA\Property(property: "token", type: "string", example: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."), + new OA\Property(property: "user", ref: "#/components/schemas/User") + ] + ) + ), + new OA\Response(response: 401, description: "Unauthorized"), + new OA\Response(response: 422, description: "Validation Error") + ] + )] + public function apiLogin() + { + $json = $this->request->getJSON(); + + if (!$json) { + return $this->failValidationErrors('Invalid JSON input'); + } + + $email = $json->email ?? ''; + $password = $json->password ?? ''; + + if (empty($email) || empty($password)) { + return $this->failValidationErrors('Email and password are required'); + } + + try { + $user = $this->userModel->where('email', $email)->first(); + + if (!$user || !password_verify($password, $user['password'])) { + return $this->failUnauthorized('Invalid credentials'); + } + + // Generate JWT token (simplified - in production use proper JWT library) + $payload = base64_encode(json_encode([ + 'user_id' => $user['id'], + 'email' => $user['email'], + 'exp' => time() + 86400 // 24 hours + ])); + + return $this->respond([ + 'success' => true, + 'token' => 'Bearer.' . $payload, + 'user' => [ + 'id' => $user['id'], + 'name' => $user['username'], + 'email' => $user['email'] + ] + ]); + } catch (\Exception $e) { + return $this->failServerError('Authentication failed'); + } + } + + /** + * API Register endpoint + */ + #[OA\Post( + path: "/auth/register", + summary: "User registration", + description: "Register a new user account", + requestBody: new OA\RequestBody( + required: true, + content: new OA\JsonContent( + required: ["name", "email", "password"], + properties: [ + new OA\Property(property: "name", type: "string", example: "John Doe"), + new OA\Property(property: "email", type: "string", format: "email", example: "user@example.com"), + new OA\Property(property: "password", type: "string", format: "password", minLength: 8, example: "password123") + ] + ) + ), + tags: ["Authentication"], + responses: [ + new OA\Response( + response: 201, + description: "Registration successful", + content: new OA\JsonContent( + properties: [ + new OA\Property(property: "success", type: "boolean", example: true), + new OA\Property(property: "message", type: "string", example: "User registered successfully"), + new OA\Property(property: "user", ref: "#/components/schemas/User") + ] + ) + ), + new OA\Response(response: 422, description: "Validation Error") + ] + )] + public function apiRegister() + { + $json = $this->request->getJSON(); + + if (!$json) { + return $this->failValidationErrors('Invalid JSON input'); + } + + $name = $json->name ?? ''; + $email = $json->email ?? ''; + $password = $json->password ?? ''; + + if (empty($name) || empty($email) || empty($password)) { + return $this->failValidationErrors('Name, email and password are required'); + } + + if (strlen($password) < 8) { + return $this->failValidationErrors('Password must be at least 8 characters'); + } + + try { + // Check if user already exists + $existingUser = $this->userModel->where('email', $email)->first(); + if ($existingUser) { + return $this->failValidationErrors('Email already registered'); + } + + // Create new user + $userData = [ + 'username' => $name, + 'email' => $email, + 'password' => password_hash($password, PASSWORD_DEFAULT), + 'created_at' => date('Y-m-d H:i:s'), + 'updated_at' => date('Y-m-d H:i:s') + ]; + + $userId = $this->userModel->insert($userData); + + if (!$userId) { + return $this->failServerError('Failed to create user'); + } + + $user = $this->userModel->find($userId); + + return $this->respondCreated([ + 'success' => true, + 'message' => 'User registered successfully', + 'user' => [ + 'id' => $user['id'], + 'name' => $user['username'], + 'email' => $user['email'] + ] + ]); + } catch (\Exception $e) { + return $this->failServerError('Registration failed'); + } + } } diff --git a/app/Controllers/Chat.php b/app/Controllers/Chat.php index 1bb8f27..1a87249 100644 --- a/app/Controllers/Chat.php +++ b/app/Controllers/Chat.php @@ -34,7 +34,7 @@ class Chat extends BaseController * * @return void */ - public function initController(\CodeIgniter\HTTP\RequestInterface $request, \CodeIgniter\HTTP\ResponseInterface $response, \Psr\Log\LoggerInterface $logger) + public function initController(\CodeIgniter\HTTP\RequestInterface $request, \CodeIgniter\HTTP\ResponseInterface $response, \Psr\Log\LoggerInterface $logger): void { parent::initController($request, $response, $logger); $this->chatModel = new ChatModel(); diff --git a/composer.json b/composer.json index cd40652..6a33e91 100644 --- a/composer.json +++ b/composer.json @@ -17,7 +17,8 @@ "require-dev": { "fakerphp/faker": "^1.9", "mikey179/vfsstream": "^1.6", - "phpunit/phpunit": "^10.5.16" + "phpunit/phpunit": "^10.5.16", + "zircote/swagger-php": "^4.0" }, "autoload": { "psr-4": { diff --git a/composer.lock b/composer.lock index 4242589..3a1e015 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "e430adb65854c5cf262b56199eca6f90", + "content-hash": "be3a0e124c02665a66910662db050873", "packages": [ { "name": "cboden/ratchet", @@ -3213,6 +3213,221 @@ ], "time": "2023-02-07T11:34:05+00:00" }, + { + "name": "symfony/finder", + "version": "v7.3.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/finder.git", + "reference": "ec2344cf77a48253bbca6939aa3d2477773ea63d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/finder/zipball/ec2344cf77a48253bbca6939aa3d2477773ea63d", + "reference": "ec2344cf77a48253bbca6939aa3d2477773ea63d", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "symfony/filesystem": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Finder\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Finds files and directories via an intuitive fluent interface", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/finder/tree/v7.3.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-12-30T19:00:26+00:00" + }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.32.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638", + "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "provide": { + "ext-ctype": "*" + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Ctype\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for ctype functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "ctype", + "polyfill", + "portable" + ], + "support": { + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.32.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-09T11:45:10+00:00" + }, + { + "name": "symfony/yaml", + "version": "v7.3.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/yaml.git", + "reference": "0c3555045a46ab3cd4cc5a69d161225195230edb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/yaml/zipball/0c3555045a46ab3cd4cc5a69d161225195230edb", + "reference": "0c3555045a46ab3cd4cc5a69d161225195230edb", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3.0", + "symfony/polyfill-ctype": "^1.8" + }, + "conflict": { + "symfony/console": "<6.4" + }, + "require-dev": { + "symfony/console": "^6.4|^7.0" + }, + "bin": [ + "Resources/bin/yaml-lint" + ], + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Yaml\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Loads and dumps YAML files", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/yaml/tree/v7.3.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-06-03T06:57:57+00:00" + }, { "name": "theseer/tokenizer", "version": "1.2.3", @@ -3262,6 +3477,87 @@ } ], "time": "2024-03-03T12:36:25+00:00" + }, + { + "name": "zircote/swagger-php", + "version": "4.11.1", + "source": { + "type": "git", + "url": "https://github.com/zircote/swagger-php.git", + "reference": "7df10e8ec47db07c031db317a25bef962b4e5de1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/zircote/swagger-php/zipball/7df10e8ec47db07c031db317a25bef962b4e5de1", + "reference": "7df10e8ec47db07c031db317a25bef962b4e5de1", + "shasum": "" + }, + "require": { + "ext-json": "*", + "php": ">=7.2", + "psr/log": "^1.1 || ^2.0 || ^3.0", + "symfony/deprecation-contracts": "^2 || ^3", + "symfony/finder": ">=2.2", + "symfony/yaml": ">=3.3" + }, + "require-dev": { + "composer/package-versions-deprecated": "^1.11", + "doctrine/annotations": "^1.7 || ^2.0", + "friendsofphp/php-cs-fixer": "^2.17 || 3.62.0", + "phpstan/phpstan": "^1.6", + "phpunit/phpunit": ">=8", + "vimeo/psalm": "^4.23" + }, + "suggest": { + "doctrine/annotations": "^1.7 || ^2.0" + }, + "bin": [ + "bin/openapi" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.x-dev" + } + }, + "autoload": { + "psr-4": { + "OpenApi\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Robert Allen", + "email": "zircote@gmail.com" + }, + { + "name": "Bob Fanger", + "email": "bfanger@gmail.com", + "homepage": "https://bfanger.nl" + }, + { + "name": "Martin Rademacher", + "email": "mano@radebatz.net", + "homepage": "https://radebatz.net" + } + ], + "description": "swagger-php - Generate interactive documentation for your RESTful API using phpdoc annotations", + "homepage": "https://github.com/zircote/swagger-php/", + "keywords": [ + "api", + "json", + "rest", + "service discovery" + ], + "support": { + "issues": "https://github.com/zircote/swagger-php/issues", + "source": "https://github.com/zircote/swagger-php/tree/4.11.1" + }, + "time": "2024-10-15T19:20:02+00:00" } ], "aliases": [], diff --git a/composer.phar b/composer.phar new file mode 100755 index 0000000..4f53d5a Binary files /dev/null and b/composer.phar differ diff --git a/docs/api-clients/README.md b/docs/api-clients/README.md new file mode 100644 index 0000000..70b7a73 --- /dev/null +++ b/docs/api-clients/README.md @@ -0,0 +1,312 @@ +# AI Chat Application API Client Libraries + +This directory contains official client libraries for the AI Chat Application API in multiple programming languages. These libraries provide a convenient way to interact with the API from your applications. + +## Available Languages + +- **JavaScript/TypeScript** - For web browsers and Node.js +- **Python** - For Python applications and scripts +- **PHP** - For PHP web applications + +## Quick Start + +### JavaScript/Node.js + +```javascript +const AiChatApiClient = require('./javascript/ai-chat-api-client.js'); + +// Initialize client +const client = new AiChatApiClient({ + baseUrl: 'https://api.example.com', + timeout: 30000 +}); + +// Login +const loginResponse = await client.login('user@example.com', 'password123'); +if (loginResponse.success) { + console.log('Logged in:', loginResponse.user); +} + +// Create conversation and send message +const conversation = await client.createConversation({ + title: 'My First Chat' +}); + +const messageResponse = await client.sendMessage( + conversation.conversation.id, + 'Hello, how can you help me today?' +); +``` + +### Python + +```python +from ai_chat_api_client import AiChatApiClient + +# Initialize client +client = AiChatApiClient( + base_url="https://api.example.com", + timeout=30 +) + +# Login +login_response = client.login("user@example.com", "password123") +if login_response.success: + print(f"Logged in: {login_response.data['user']}") + +# Create conversation and send message +conversation_response = client.create_conversation( + title="My First Chat" +) + +if conversation_response.success: + conversation_id = conversation_response.data['conversation']['id'] + + message_response = client.send_message( + conversation_id, + "Hello, how can you help me today?" + ) +``` + +### PHP + +```php +use AiChat\ApiClient\AiChatApiClient; + +// Initialize client +$client = new AiChatApiClient('https://api.example.com'); + +// Login +$loginResponse = $client->login('user@example.com', 'password123'); +if ($loginResponse->success) { + echo "Logged in: " . json_encode($loginResponse->data['user']) . "\n"; +} + +// Create conversation and send message +$conversationResponse = $client->createConversation('My First Chat'); +if ($conversationResponse->success) { + $conversationId = $conversationResponse->data['conversation']['id']; + + $messageResponse = $client->sendMessage( + $conversationId, + 'Hello, how can you help me today?' + ); +} +``` + +## Features + +All client libraries provide the following features: + +### Authentication +- User login/logout +- User registration +- JWT token management +- Current user information + +### Conversation Management +- List conversations with pagination +- Create new conversations +- Get conversation details with messages +- Update conversation metadata +- Delete conversations + +### Messaging +- Send messages to conversations +- Get message history with pagination +- Support for file attachments + +### File Upload +- Upload files to conversations +- Support for various file types + +### Real-time Communication +- WebSocket support for real-time updates +- Event-driven message handling + +## Installation + +### JavaScript/Node.js + +```bash +# No installation required - just include the file +# For Node.js projects, you may need: +npm install node-fetch # If using Node.js < 18 +``` + +### Python + +```bash +# Install dependencies +pip install requests websocket-client +``` + +### PHP + +```bash +# Install via Composer +composer require guzzlehttp/guzzle +``` + +## Configuration + +### Base URL +Set the base URL of your API server: + +- **Development**: `http://localhost:8080` +- **Production**: `https://your-api-domain.com` + +### Authentication +All clients support JWT token authentication. Tokens are automatically managed after login. + +### Timeout +Configure request timeouts based on your needs: + +- **Default**: 30 seconds +- **File uploads**: Consider increasing for large files +- **WebSocket**: Persistent connection + +## Error Handling + +All clients provide consistent error handling: + +### JavaScript +```javascript +try { + const response = await client.login(email, password); + if (!response.success) { + console.error('Login failed:', response.message); + } +} catch (error) { + console.error('Request failed:', error.message); +} +``` + +### Python +```python +response = client.login(email, password) +if not response.success: + print(f"Login failed: {response.message}") + if response.errors: + print(f"Errors: {response.errors}") +``` + +### PHP +```php +$response = $client->login($email, $password); +if (!$response->success) { + echo "Login failed: " . $response->message . "\n"; + if ($response->errors) { + echo "Errors: " . json_encode($response->errors) . "\n"; + } +} +``` + +## WebSocket Usage + +### JavaScript +```javascript +const ws = client.connectWebSocket({ + onMessage: (data) => { + console.log('New message:', data); + }, + onError: (error) => { + console.error('WebSocket error:', error); + }, + onClose: () => { + console.log('WebSocket closed'); + } +}); +``` + +### Python +```python +from ai_chat_api_client import AiChatWebSocketClient + +ws_client = AiChatWebSocketClient( + base_url="wss://api.example.com", + token=client.token +) + +def on_message(data): + print(f"New message: {data}") + +def on_error(error): + print(f"WebSocket error: {error}") + +ws_client.on_message = on_message +ws_client.on_error = on_error + +ws_client.connect() +ws_client.run_forever() +``` + +## File Upload Example + +### JavaScript +```javascript +const fileInput = document.getElementById('file-input'); +const file = fileInput.files[0]; + +const uploadResponse = await client.uploadFile(file, conversationId); +if (uploadResponse.success) { + console.log('File uploaded:', uploadResponse.file); +} +``` + +### Python +```python +upload_response = client.upload_file('/path/to/file.pdf', conversation_id) +if upload_response.success: + print(f"File uploaded: {upload_response.data['file']}") +``` + +### PHP +```php +$uploadResponse = $client->uploadFile('/path/to/file.pdf', $conversationId); +if ($uploadResponse->success) { + echo "File uploaded: " . json_encode($uploadResponse->data['file']) . "\n"; +} +``` + +## Best Practices + +### Token Management +- Store tokens securely (localStorage for web, secure storage for mobile) +- Handle token expiration gracefully +- Implement automatic token refresh if available + +### Error Handling +- Always check response success status +- Log errors appropriately +- Provide user-friendly error messages + +### Rate Limiting +- Implement client-side rate limiting if needed +- Handle 429 responses appropriately +- Use exponential backoff for retries + +### File Uploads +- Validate file types and sizes before upload +- Show upload progress for large files +- Handle upload failures gracefully + +## Examples + +See the `/examples` directory for complete working examples in each language: + +- `javascript/complete-chat-app.html` - Full chat application +- `python/chat_bot.py` - Command-line chat bot +- `php/chat_integration.php` - Web application integration + +## Support + +For questions and support: + +- Check the [API Documentation](../api-docs.html) +- Review the [OpenAPI Specification](../openapi.yaml) +- Create an issue in the project repository + +## License + +These client libraries are released under the same license as the main project. \ No newline at end of file diff --git a/docs/api-clients/javascript/ai-chat-api-client.js b/docs/api-clients/javascript/ai-chat-api-client.js new file mode 100644 index 0000000..fa0b010 --- /dev/null +++ b/docs/api-clients/javascript/ai-chat-api-client.js @@ -0,0 +1,357 @@ +/** + * AI Chat Application API Client (JavaScript) + * + * A comprehensive client library for interacting with the AI Chat Application API. + * Supports authentication, conversation management, and real-time messaging. + * + * @version 1.0.0 + * @author AI Chat Team + */ + +class AiChatApiClient { + /** + * Initialize the API client + * @param {Object} config - Configuration options + * @param {string} config.baseUrl - Base URL of the API + * @param {string} [config.token] - JWT authentication token + * @param {number} [config.timeout=30000] - Request timeout in milliseconds + */ + constructor(config) { + this.baseUrl = config.baseUrl || 'http://localhost:8080'; + this.token = config.token || null; + this.timeout = config.timeout || 30000; + + // Default headers + this.defaultHeaders = { + 'Content-Type': 'application/json', + 'Accept': 'application/json' + }; + } + + /** + * Set the authentication token + * @param {string} token - JWT token + */ + setToken(token) { + this.token = token; + } + + /** + * Get headers with authentication + * @returns {Object} Headers object + */ + getHeaders() { + const headers = { ...this.defaultHeaders }; + if (this.token) { + headers['Authorization'] = `Bearer ${this.token}`; + } + return headers; + } + + /** + * Make HTTP request + * @param {string} method - HTTP method + * @param {string} endpoint - API endpoint + * @param {Object} [data] - Request data + * @returns {Promise} Response data + */ + async request(method, endpoint, data = null) { + const url = `${this.baseUrl}${endpoint}`; + const config = { + method: method.toUpperCase(), + headers: this.getHeaders(), + timeout: this.timeout + }; + + if (data && ['POST', 'PUT', 'PATCH'].includes(config.method)) { + config.body = JSON.stringify(data); + } + + try { + const response = await fetch(url, config); + const responseData = await response.json(); + + if (!response.ok) { + throw new Error(responseData.message || `HTTP ${response.status}`); + } + + return responseData; + } catch (error) { + console.error('API Request Error:', error); + throw error; + } + } + + // Authentication Methods + + /** + * Login user + * @param {string} email - User email + * @param {string} password - User password + * @returns {Promise} Login response with token and user info + */ + async login(email, password) { + const response = await this.request('POST', '/auth/login', { + email, + password + }); + + if (response.success && response.token) { + this.setToken(response.token); + } + + return response; + } + + /** + * Register new user + * @param {string} name - User name + * @param {string} email - User email + * @param {string} password - User password + * @returns {Promise} Registration response + */ + async register(name, email, password) { + return await this.request('POST', '/auth/register', { + name, + email, + password + }); + } + + /** + * Logout user + * @returns {Promise} Logout response + */ + async logout() { + const response = await this.request('POST', '/auth/logout'); + this.token = null; + return response; + } + + /** + * Get current user information + * @returns {Promise} User information + */ + async getCurrentUser() { + return await this.request('GET', '/auth/me'); + } + + // Conversation Methods + + /** + * Get all conversations + * @param {Object} [params] - Query parameters + * @param {number} [params.page=1] - Page number + * @param {number} [params.limit=20] - Items per page + * @returns {Promise} Conversations list with pagination + */ + async getConversations(params = {}) { + const query = new URLSearchParams({ + page: params.page || 1, + limit: params.limit || 20 + }); + + return await this.request('GET', `/chat/conversations?${query}`); + } + + /** + * Create new conversation + * @param {Object} data - Conversation data + * @param {string} [data.title] - Conversation title + * @param {string} [data.description] - Conversation description + * @returns {Promise} Created conversation + */ + async createConversation(data = {}) { + return await this.request('POST', '/chat/conversations', data); + } + + /** + * Get specific conversation with messages + * @param {number} conversationId - Conversation ID + * @returns {Promise} Conversation with messages + */ + async getConversation(conversationId) { + return await this.request('GET', `/chat/conversations/${conversationId}`); + } + + /** + * Update conversation + * @param {number} conversationId - Conversation ID + * @param {Object} data - Update data + * @param {string} [data.title] - New title + * @param {string} [data.description] - New description + * @returns {Promise} Updated conversation + */ + async updateConversation(conversationId, data) { + return await this.request('PUT', `/chat/conversations/${conversationId}`, data); + } + + /** + * Delete conversation + * @param {number} conversationId - Conversation ID + * @returns {Promise} Deletion response + */ + async deleteConversation(conversationId) { + return await this.request('DELETE', `/chat/conversations/${conversationId}`); + } + + // Message Methods + + /** + * Get messages for a conversation + * @param {number} conversationId - Conversation ID + * @param {Object} [params] - Query parameters + * @param {number} [params.page=1] - Page number + * @param {number} [params.limit=50] - Items per page + * @returns {Promise} Messages list with pagination + */ + async getMessages(conversationId, params = {}) { + const query = new URLSearchParams({ + page: params.page || 1, + limit: params.limit || 50 + }); + + return await this.request('GET', `/chat/conversations/${conversationId}/messages?${query}`); + } + + /** + * Send message in conversation + * @param {number} conversationId - Conversation ID + * @param {Object} data - Message data + * @param {string} data.content - Message content + * @param {string[]} [data.attachments] - File attachments + * @returns {Promise} Sent message and AI response + */ + async sendMessage(conversationId, data) { + return await this.request('POST', `/chat/conversations/${conversationId}/messages`, data); + } + + // File Upload Methods + + /** + * Upload file + * @param {File} file - File to upload + * @param {number} [conversationId] - Associated conversation ID + * @returns {Promise} Upload response + */ + async uploadFile(file, conversationId = null) { + const formData = new FormData(); + formData.append('file', file); + + if (conversationId) { + formData.append('conversation_id', conversationId); + } + + const url = `${this.baseUrl}/chat/upload`; + const headers = { ...this.getHeaders() }; + delete headers['Content-Type']; // Let browser set it for FormData + + try { + const response = await fetch(url, { + method: 'POST', + headers, + body: formData + }); + + const responseData = await response.json(); + + if (!response.ok) { + throw new Error(responseData.message || `HTTP ${response.status}`); + } + + return responseData; + } catch (error) { + console.error('File Upload Error:', error); + throw error; + } + } + + // WebSocket Methods + + /** + * Connect to WebSocket for real-time chat + * @param {Object} [options] - WebSocket options + * @param {Function} [options.onMessage] - Message handler + * @param {Function} [options.onError] - Error handler + * @param {Function} [options.onClose] - Close handler + * @returns {WebSocket} WebSocket connection + */ + connectWebSocket(options = {}) { + const wsUrl = this.baseUrl.replace(/^http/, 'ws') + '/chat/stream'; + const ws = new WebSocket(wsUrl); + + ws.onopen = function(event) { + console.log('WebSocket connected'); + if (options.onOpen) options.onOpen(event); + }; + + ws.onmessage = function(event) { + try { + const data = JSON.parse(event.data); + if (options.onMessage) options.onMessage(data); + } catch (e) { + console.error('WebSocket message parse error:', e); + } + }; + + ws.onerror = function(event) { + console.error('WebSocket error:', event); + if (options.onError) options.onError(event); + }; + + ws.onclose = function(event) { + console.log('WebSocket closed'); + if (options.onClose) options.onClose(event); + }; + + return ws; + } +} + +// Export for different module systems +if (typeof module !== 'undefined' && module.exports) { + module.exports = AiChatApiClient; +} else if (typeof define === 'function' && define.amd) { + define([], function() { return AiChatApiClient; }); +} else { + window.AiChatApiClient = AiChatApiClient; +} + +/** + * Usage Examples: + * + * // Initialize client + * const client = new AiChatApiClient({ + * baseUrl: 'https://api.example.com', + * timeout: 30000 + * }); + * + * // Login + * try { + * const loginResponse = await client.login('user@example.com', 'password123'); + * console.log('Logged in:', loginResponse.user); + * } catch (error) { + * console.error('Login failed:', error.message); + * } + * + * // Create conversation + * const conversation = await client.createConversation({ + * title: 'My First Chat', + * description: 'Learning about AI' + * }); + * + * // Send message + * const messageResponse = await client.sendMessage(conversation.conversation.id, { + * content: 'Hello, how can you help me today?' + * }); + * + * // Connect WebSocket for real-time updates + * const ws = client.connectWebSocket({ + * onMessage: (data) => { + * console.log('New message:', data); + * }, + * onError: (error) => { + * console.error('WebSocket error:', error); + * } + * }); + */ \ No newline at end of file diff --git a/docs/api-clients/php/AiChatApiClient.php b/docs/api-clients/php/AiChatApiClient.php new file mode 100644 index 0000000..cb9dd7e --- /dev/null +++ b/docs/api-clients/php/AiChatApiClient.php @@ -0,0 +1,462 @@ +success = $success; + $this->data = $data; + $this->message = $message; + $this->errors = $errors; + } +} + +/** + * AI Chat Application API Client + * + * A comprehensive client for interacting with the AI Chat Application API. + * Provides methods for authentication, conversation management, messaging, and file uploads. + */ +class AiChatApiClient +{ + private string $baseUrl; + private ?string $token; + private Client $httpClient; + private int $timeout; + + /** + * Initialize the API client + * + * @param string $baseUrl Base URL of the API + * @param string|null $token JWT authentication token + * @param int $timeout Request timeout in seconds + */ + public function __construct(string $baseUrl = 'http://localhost:8080', ?string $token = null, int $timeout = 30) + { + $this->baseUrl = rtrim($baseUrl, '/'); + $this->token = $token; + $this->timeout = $timeout; + + $this->httpClient = new Client([ + 'base_uri' => $this->baseUrl, + 'timeout' => $this->timeout, + 'headers' => [ + 'Content-Type' => 'application/json', + 'Accept' => 'application/json', + ] + ]); + } + + /** + * Set the authentication token + * + * @param string|null $token JWT token + */ + public function setToken(?string $token): void + { + $this->token = $token; + } + + /** + * Get headers with authentication + * + * @return array Headers array + */ + private function getHeaders(): array + { + $headers = [ + 'Content-Type' => 'application/json', + 'Accept' => 'application/json', + ]; + + if ($this->token) { + $headers['Authorization'] = 'Bearer ' . $this->token; + } + + return $headers; + } + + /** + * Make HTTP request to the API + * + * @param string $method HTTP method + * @param string $endpoint API endpoint + * @param array|null $data Request data + * @param array|null $queryParams Query parameters + * @param array|null $files Files to upload + * @return ApiResponse API response + */ + private function makeRequest(string $method, string $endpoint, ?array $data = null, ?array $queryParams = null, ?array $files = null): ApiResponse + { + try { + $options = [ + 'headers' => $this->getHeaders(), + ]; + + if ($queryParams) { + $options['query'] = $queryParams; + } + + if ($files) { + // Handle file uploads + $multipart = []; + + if ($data) { + foreach ($data as $key => $value) { + $multipart[] = [ + 'name' => $key, + 'contents' => $value, + ]; + } + } + + foreach ($files as $key => $file) { + $multipart[] = [ + 'name' => $key, + 'contents' => fopen($file, 'r'), + 'filename' => basename($file), + ]; + } + + $options['multipart'] = $multipart; + // Remove Content-Type header for multipart + unset($options['headers']['Content-Type']); + } elseif ($data) { + $options['json'] = $data; + } + + $response = $this->httpClient->request($method, $endpoint, $options); + $responseData = json_decode($response->getBody()->getContents(), true); + + return new ApiResponse( + success: $responseData['success'] ?? true, + data: $responseData, + message: $responseData['message'] ?? null, + errors: $responseData['errors'] ?? null + ); + + } catch (RequestException $e) { + $responseData = null; + $message = $e->getMessage(); + + if ($e->hasResponse()) { + try { + $responseData = json_decode($e->getResponse()->getBody()->getContents(), true); + $message = $responseData['message'] ?? $message; + } catch (Exception $jsonException) { + // Use original message if JSON parsing fails + } + } + + return new ApiResponse( + success: false, + data: $responseData, + message: $message, + errors: $responseData['errors'] ?? null + ); + } + } + + // Authentication Methods + + /** + * Login user + * + * @param string $email User email + * @param string $password User password + * @return ApiResponse Login response with token and user info + */ + public function login(string $email, string $password): ApiResponse + { + $response = $this->makeRequest('POST', '/auth/login', [ + 'email' => $email, + 'password' => $password, + ]); + + if ($response->success && isset($response->data['token'])) { + $this->setToken($response->data['token']); + } + + return $response; + } + + /** + * Register new user + * + * @param string $name User name + * @param string $email User email + * @param string $password User password + * @return ApiResponse Registration response + */ + public function register(string $name, string $email, string $password): ApiResponse + { + return $this->makeRequest('POST', '/auth/register', [ + 'name' => $name, + 'email' => $email, + 'password' => $password, + ]); + } + + /** + * Logout user + * + * @return ApiResponse Logout response + */ + public function logout(): ApiResponse + { + $response = $this->makeRequest('POST', '/auth/logout'); + if ($response->success) { + $this->setToken(null); + } + return $response; + } + + /** + * Get current user information + * + * @return ApiResponse User information + */ + public function getCurrentUser(): ApiResponse + { + return $this->makeRequest('GET', '/auth/me'); + } + + // Conversation Methods + + /** + * Get all conversations + * + * @param int $page Page number + * @param int $limit Items per page + * @return ApiResponse Conversations list with pagination + */ + public function getConversations(int $page = 1, int $limit = 20): ApiResponse + { + $queryParams = ['page' => $page, 'limit' => $limit]; + return $this->makeRequest('GET', '/chat/conversations', null, $queryParams); + } + + /** + * Create new conversation + * + * @param string|null $title Conversation title + * @param string|null $description Conversation description + * @return ApiResponse Created conversation + */ + public function createConversation(?string $title = null, ?string $description = null): ApiResponse + { + $data = []; + if ($title !== null) { + $data['title'] = $title; + } + if ($description !== null) { + $data['description'] = $description; + } + + return $this->makeRequest('POST', '/chat/conversations', $data); + } + + /** + * Get specific conversation with messages + * + * @param int $conversationId Conversation ID + * @return ApiResponse Conversation with messages + */ + public function getConversation(int $conversationId): ApiResponse + { + return $this->makeRequest('GET', "/chat/conversations/{$conversationId}"); + } + + /** + * Update conversation + * + * @param int $conversationId Conversation ID + * @param string|null $title New title + * @param string|null $description New description + * @return ApiResponse Updated conversation + */ + public function updateConversation(int $conversationId, ?string $title = null, ?string $description = null): ApiResponse + { + $data = []; + if ($title !== null) { + $data['title'] = $title; + } + if ($description !== null) { + $data['description'] = $description; + } + + return $this->makeRequest('PUT', "/chat/conversations/{$conversationId}", $data); + } + + /** + * Delete conversation + * + * @param int $conversationId Conversation ID + * @return ApiResponse Deletion response + */ + public function deleteConversation(int $conversationId): ApiResponse + { + return $this->makeRequest('DELETE', "/chat/conversations/{$conversationId}"); + } + + // Message Methods + + /** + * Get messages for a conversation + * + * @param int $conversationId Conversation ID + * @param int $page Page number + * @param int $limit Items per page + * @return ApiResponse Messages list with pagination + */ + public function getMessages(int $conversationId, int $page = 1, int $limit = 50): ApiResponse + { + $queryParams = ['page' => $page, 'limit' => $limit]; + return $this->makeRequest('GET', "/chat/conversations/{$conversationId}/messages", null, $queryParams); + } + + /** + * Send message in conversation + * + * @param int $conversationId Conversation ID + * @param string $content Message content + * @param array|null $attachments File attachments + * @return ApiResponse Sent message and AI response + */ + public function sendMessage(int $conversationId, string $content, ?array $attachments = null): ApiResponse + { + $data = ['content' => $content]; + if ($attachments) { + $data['attachments'] = $attachments; + } + + return $this->makeRequest('POST', "/chat/conversations/{$conversationId}/messages", $data); + } + + // File Upload Methods + + /** + * Upload file + * + * @param string $filePath Path to file to upload + * @param int|null $conversationId Associated conversation ID + * @return ApiResponse Upload response + */ + public function uploadFile(string $filePath, ?int $conversationId = null): ApiResponse + { + if (!file_exists($filePath)) { + return new ApiResponse(false, null, 'File does not exist: ' . $filePath); + } + + $data = []; + if ($conversationId !== null) { + $data['conversation_id'] = $conversationId; + } + + return $this->makeRequest('POST', '/chat/upload', $data, null, ['file' => $filePath]); + } +} + +/** + * Example usage and helper functions + */ +class AiChatApiClientHelper +{ + /** + * Create a client instance with common configuration + * + * @param string $baseUrl API base URL + * @param string|null $token JWT token + * @return AiChatApiClient Configured client instance + */ + public static function create(string $baseUrl = 'http://localhost:8080', ?string $token = null): AiChatApiClient + { + return new AiChatApiClient($baseUrl, $token); + } + + /** + * Handle API response and extract data or throw exception + * + * @param ApiResponse $response API response + * @return mixed Response data + * @throws Exception If response indicates failure + */ + public static function handleResponse(ApiResponse $response): mixed + { + if (!$response->success) { + $message = $response->message ?: 'API request failed'; + if ($response->errors) { + $message .= '. Errors: ' . json_encode($response->errors); + } + throw new Exception($message); + } + + return $response->data; + } +} + +// Example usage +if (basename(__FILE__) === basename($_SERVER['SCRIPT_NAME'] ?? '')) { + try { + // Initialize client + $client = new AiChatApiClient('https://api.example.com'); + + // Login + $loginResponse = $client->login('user@example.com', 'password123'); + if ($loginResponse->success) { + echo "Logged in: " . json_encode($loginResponse->data['user']) . "\n"; + } else { + echo "Login failed: " . $loginResponse->message . "\n"; + } + + // Create conversation + $conversationResponse = $client->createConversation('My First Chat', 'Learning about AI'); + if ($conversationResponse->success) { + $conversationId = $conversationResponse->data['conversation']['id']; + + // Send message + $messageResponse = $client->sendMessage( + $conversationId, + 'Hello, how can you help me today?' + ); + + if ($messageResponse->success) { + echo "Message sent: " . json_encode($messageResponse->data) . "\n"; + } + } + + // Get conversations + $conversationsResponse = $client->getConversations(1, 10); + if ($conversationsResponse->success) { + echo "Found " . count($conversationsResponse->data['conversations']) . " conversations\n"; + } + + } catch (Exception $e) { + echo "Error: " . $e->getMessage() . "\n"; + } +} \ No newline at end of file diff --git a/docs/api-clients/python/ai_chat_api_client.py b/docs/api-clients/python/ai_chat_api_client.py new file mode 100644 index 0000000..7f7b1c0 --- /dev/null +++ b/docs/api-clients/python/ai_chat_api_client.py @@ -0,0 +1,475 @@ +""" +AI Chat Application API Client (Python) + +A comprehensive client library for interacting with the AI Chat Application API. +Supports authentication, conversation management, and real-time messaging. + +Version: 1.0.0 +Author: AI Chat Team +""" + +import json +import requests +import websocket +from typing import Dict, List, Optional, Union, Any, Callable +from dataclasses import dataclass +from urllib.parse import urljoin, urlencode + + +@dataclass +class ApiResponse: + """Standard API response wrapper""" + success: bool + data: Any + message: Optional[str] = None + errors: Optional[Dict[str, List[str]]] = None + + +class AiChatApiClient: + """ + AI Chat Application API Client + + A comprehensive client for interacting with the AI Chat Application API. + Provides methods for authentication, conversation management, messaging, and file uploads. + """ + + def __init__(self, base_url: str = "http://localhost:8080", + token: Optional[str] = None, timeout: int = 30): + """ + Initialize the API client + + Args: + base_url: Base URL of the API + token: JWT authentication token + timeout: Request timeout in seconds + """ + self.base_url = base_url.rstrip('/') + self.token = token + self.timeout = timeout + self.session = requests.Session() + + # Set default headers + self.session.headers.update({ + 'Content-Type': 'application/json', + 'Accept': 'application/json' + }) + + def set_token(self, token: str) -> None: + """Set the authentication token""" + self.token = token + if token: + self.session.headers['Authorization'] = f'Bearer {token}' + elif 'Authorization' in self.session.headers: + del self.session.headers['Authorization'] + + def _make_request(self, method: str, endpoint: str, + data: Optional[Dict] = None, + params: Optional[Dict] = None, + files: Optional[Dict] = None) -> ApiResponse: + """ + Make HTTP request to the API + + Args: + method: HTTP method + endpoint: API endpoint + data: Request data + params: Query parameters + files: Files to upload + + Returns: + ApiResponse object + + Raises: + requests.RequestException: For HTTP errors + """ + url = urljoin(self.base_url, endpoint) + + request_kwargs = { + 'timeout': self.timeout, + 'params': params + } + + if files: + # Remove Content-Type for file uploads + headers = dict(self.session.headers) + if 'Content-Type' in headers: + del headers['Content-Type'] + request_kwargs['headers'] = headers + request_kwargs['files'] = files + if data: + request_kwargs['data'] = data + elif data: + request_kwargs['json'] = data + + try: + response = self.session.request(method, url, **request_kwargs) + response.raise_for_status() + + response_data = response.json() + return ApiResponse( + success=response_data.get('success', True), + data=response_data, + message=response_data.get('message'), + errors=response_data.get('errors') + ) + + except requests.exceptions.HTTPError as e: + try: + error_data = response.json() + return ApiResponse( + success=False, + data=error_data, + message=error_data.get('message', str(e)), + errors=error_data.get('errors') + ) + except json.JSONDecodeError: + return ApiResponse( + success=False, + data=None, + message=str(e) + ) + except requests.exceptions.RequestException as e: + return ApiResponse( + success=False, + data=None, + message=str(e) + ) + + # Authentication Methods + + def login(self, email: str, password: str) -> ApiResponse: + """ + Login user + + Args: + email: User email + password: User password + + Returns: + ApiResponse with token and user info + """ + response = self._make_request('POST', '/auth/login', { + 'email': email, + 'password': password + }) + + if response.success and response.data.get('token'): + self.set_token(response.data['token']) + + return response + + def register(self, name: str, email: str, password: str) -> ApiResponse: + """ + Register new user + + Args: + name: User name + email: User email + password: User password + + Returns: + ApiResponse with registration result + """ + return self._make_request('POST', '/auth/register', { + 'name': name, + 'email': email, + 'password': password + }) + + def logout(self) -> ApiResponse: + """ + Logout user + + Returns: + ApiResponse with logout result + """ + response = self._make_request('POST', '/auth/logout') + if response.success: + self.set_token(None) + return response + + def get_current_user(self) -> ApiResponse: + """ + Get current user information + + Returns: + ApiResponse with user information + """ + return self._make_request('GET', '/auth/me') + + # Conversation Methods + + def get_conversations(self, page: int = 1, limit: int = 20) -> ApiResponse: + """ + Get all conversations + + Args: + page: Page number + limit: Items per page + + Returns: + ApiResponse with conversations list and pagination + """ + params = {'page': page, 'limit': limit} + return self._make_request('GET', '/chat/conversations', params=params) + + def create_conversation(self, title: Optional[str] = None, + description: Optional[str] = None) -> ApiResponse: + """ + Create new conversation + + Args: + title: Conversation title + description: Conversation description + + Returns: + ApiResponse with created conversation + """ + data = {} + if title: + data['title'] = title + if description: + data['description'] = description + + return self._make_request('POST', '/chat/conversations', data) + + def get_conversation(self, conversation_id: int) -> ApiResponse: + """ + Get specific conversation with messages + + Args: + conversation_id: Conversation ID + + Returns: + ApiResponse with conversation and messages + """ + return self._make_request('GET', f'/chat/conversations/{conversation_id}') + + def update_conversation(self, conversation_id: int, + title: Optional[str] = None, + description: Optional[str] = None) -> ApiResponse: + """ + Update conversation + + Args: + conversation_id: Conversation ID + title: New title + description: New description + + Returns: + ApiResponse with updated conversation + """ + data = {} + if title: + data['title'] = title + if description: + data['description'] = description + + return self._make_request('PUT', f'/chat/conversations/{conversation_id}', data) + + def delete_conversation(self, conversation_id: int) -> ApiResponse: + """ + Delete conversation + + Args: + conversation_id: Conversation ID + + Returns: + ApiResponse with deletion result + """ + return self._make_request('DELETE', f'/chat/conversations/{conversation_id}') + + # Message Methods + + def get_messages(self, conversation_id: int, page: int = 1, + limit: int = 50) -> ApiResponse: + """ + Get messages for a conversation + + Args: + conversation_id: Conversation ID + page: Page number + limit: Items per page + + Returns: + ApiResponse with messages list and pagination + """ + params = {'page': page, 'limit': limit} + return self._make_request('GET', f'/chat/conversations/{conversation_id}/messages', + params=params) + + def send_message(self, conversation_id: int, content: str, + attachments: Optional[List[str]] = None) -> ApiResponse: + """ + Send message in conversation + + Args: + conversation_id: Conversation ID + content: Message content + attachments: File attachments + + Returns: + ApiResponse with sent message and AI response + """ + data = {'content': content} + if attachments: + data['attachments'] = attachments + + return self._make_request('POST', f'/chat/conversations/{conversation_id}/messages', data) + + # File Upload Methods + + def upload_file(self, file_path: str, conversation_id: Optional[int] = None) -> ApiResponse: + """ + Upload file + + Args: + file_path: Path to file to upload + conversation_id: Associated conversation ID + + Returns: + ApiResponse with upload result + """ + data = {} + if conversation_id: + data['conversation_id'] = conversation_id + + with open(file_path, 'rb') as f: + files = {'file': f} + return self._make_request('POST', '/chat/upload', data=data, files=files) + + +class AiChatWebSocketClient: + """ + WebSocket client for real-time chat functionality + """ + + def __init__(self, base_url: str = "http://localhost:8080", token: Optional[str] = None): + """ + Initialize WebSocket client + + Args: + base_url: Base URL of the API + token: JWT authentication token + """ + self.base_url = base_url.replace('http', 'ws', 1) + self.token = token + self.ws = None + + # Event handlers + self.on_message: Optional[Callable] = None + self.on_error: Optional[Callable] = None + self.on_close: Optional[Callable] = None + self.on_open: Optional[Callable] = None + + def connect(self) -> None: + """Connect to WebSocket""" + ws_url = f"{self.base_url}/chat/stream" + + headers = {} + if self.token: + headers['Authorization'] = f'Bearer {self.token}' + + self.ws = websocket.WebSocketApp( + ws_url, + header=headers, + on_message=self._on_message, + on_error=self._on_error, + on_close=self._on_close, + on_open=self._on_open + ) + + def run_forever(self) -> None: + """Run WebSocket connection forever""" + if self.ws: + self.ws.run_forever() + + def close(self) -> None: + """Close WebSocket connection""" + if self.ws: + self.ws.close() + + def send_message(self, data: Dict) -> None: + """Send message through WebSocket""" + if self.ws: + self.ws.send(json.dumps(data)) + + def _on_message(self, ws, message): + """Internal message handler""" + try: + data = json.loads(message) + if self.on_message: + self.on_message(data) + except json.JSONDecodeError as e: + if self.on_error: + self.on_error(f"JSON decode error: {e}") + + def _on_error(self, ws, error): + """Internal error handler""" + if self.on_error: + self.on_error(error) + + def _on_close(self, ws, close_status_code, close_msg): + """Internal close handler""" + if self.on_close: + self.on_close(close_status_code, close_msg) + + def _on_open(self, ws): + """Internal open handler""" + if self.on_open: + self.on_open() + + +# Example usage +if __name__ == "__main__": + # Initialize client + client = AiChatApiClient( + base_url="https://api.example.com", + timeout=30 + ) + + # Login + try: + login_response = client.login("user@example.com", "password123") + if login_response.success: + print(f"Logged in: {login_response.data['user']}") + else: + print(f"Login failed: {login_response.message}") + except Exception as e: + print(f"Login error: {e}") + + # Create conversation + conversation_response = client.create_conversation( + title="My First Chat", + description="Learning about AI" + ) + + if conversation_response.success: + conversation_id = conversation_response.data['conversation']['id'] + + # Send message + message_response = client.send_message( + conversation_id, + "Hello, how can you help me today?" + ) + + if message_response.success: + print(f"Message sent: {message_response.data}") + + # WebSocket example + ws_client = AiChatWebSocketClient( + base_url="wss://api.example.com", + token=client.token + ) + + def on_message(data): + print(f"New message: {data}") + + def on_error(error): + print(f"WebSocket error: {error}") + + ws_client.on_message = on_message + ws_client.on_error = on_error + + ws_client.connect() + # ws_client.run_forever() # Uncomment to run WebSocket \ No newline at end of file diff --git a/docs/api-docs-redoc.html b/docs/api-docs-redoc.html new file mode 100644 index 0000000..110b037 --- /dev/null +++ b/docs/api-docs-redoc.html @@ -0,0 +1,28 @@ + + + + AI Chat Application API - Redoc + + + + + + +
+

🤖 AI Chat Application API

+

Comprehensive API Documentation

+
+ + + + \ No newline at end of file diff --git a/docs/api-docs.html b/docs/api-docs.html new file mode 100644 index 0000000..707da57 --- /dev/null +++ b/docs/api-docs.html @@ -0,0 +1,182 @@ + + + + + + AI Chat Application API Documentation + + + + +
+

🤖 AI Chat Application API

+

Interactive API Documentation

+
+ +
+
+
+

📋 API Features

+
    +
  • User Authentication & Authorization
  • +
  • Real-time Chat Messaging
  • +
  • Conversation Management
  • +
  • File Upload Support
  • +
  • WebSocket Integration
  • +
  • Pagination & Filtering
  • +
+
+ +
+

🔐 Authentication

+

This API uses JWT Bearer token authentication. Include the token in your requests:

+
+ Authorization: Bearer YOUR_JWT_TOKEN +
+
+ +
+

🌐 Base URLs

+

Development: http://localhost:8080

+

Production: https://api.example.com

+
+ +
+

📚 Getting Started

+
    +
  1. Register a new user account
  2. +
  3. Login to get your JWT token
  4. +
  5. Use the token to access protected endpoints
  6. +
  7. Start creating conversations and sending messages
  8. +
+
+
+
+ +
+ + + + + + \ No newline at end of file diff --git a/docs/api-documentation-index.html b/docs/api-documentation-index.html new file mode 100644 index 0000000..c543313 --- /dev/null +++ b/docs/api-documentation-index.html @@ -0,0 +1,469 @@ + + + + + + AI Chat Application - API Documentation + + + +
+
+

🤖 AI Chat Application

+

Comprehensive API Documentation & Client Libraries

+
+ +
+
+
📚
+

Interactive API Documentation

+

Explore the API with Swagger UI - test endpoints, view schemas, and see examples in real-time.

+
    +
  • Try endpoints directly in the browser
  • +
  • View request/response schemas
  • +
  • Authentication testing
  • +
  • Real-time examples
  • +
+ Open Swagger UI + Open Redoc +
+ +
+
📖
+

OpenAPI Specification

+

Download or view the complete OpenAPI 3.0 specification for the AI Chat Application API.

+
    +
  • Complete endpoint documentation
  • +
  • Schema definitions
  • +
  • Authentication details
  • +
  • Error response formats
  • +
+ View YAML + View JSON +
+ +
+
+

Client Libraries

+

Official client libraries for popular programming languages with full API support.

+
    +
  • JavaScript/TypeScript
  • +
  • Python
  • +
  • PHP
  • +
  • WebSocket support
  • +
+ Browse Clients + Documentation +
+ +
+
🚀
+

Getting Started Guide

+

Step-by-step guide to get started with the AI Chat Application API.

+
    +
  • Authentication setup
  • +
  • First API calls
  • +
  • Common patterns
  • +
  • Best practices
  • +
+ View Guide + User Guide +
+
+ +
+

🚀 Quick Start

+

Get started with the AI Chat API in just a few lines of code:

+ +
+
+ + + + +
+ +
+
// Initialize the client
+const client = new AiChatApiClient({
+    baseUrl: 'https://api.example.com'
+});
+
+// Login
+const loginResponse = await client.login(
+    'user@example.com', 
+    'password123'
+);
+
+// Create conversation
+const conversation = await client.createConversation({
+    title: 'My First Chat'
+});
+
+// Send message
+const messageResponse = await client.sendMessage(
+    conversation.conversation.id,
+    'Hello, how can you help me today?'
+);
+
+console.log('AI Response:', messageResponse.ai_response);
+
+ +
+
# Initialize the client
+client = AiChatApiClient(
+    base_url="https://api.example.com"
+)
+
+# Login
+login_response = client.login(
+    "user@example.com", 
+    "password123"
+)
+
+# Create conversation
+conversation_response = client.create_conversation(
+    title="My First Chat"
+)
+
+# Send message
+message_response = client.send_message(
+    conversation_response.data['conversation']['id'],
+    "Hello, how can you help me today?"
+)
+
+print("AI Response:", message_response.data['ai_response'])
+
+ +
+
// Initialize the client
+$client = new AiChatApiClient('https://api.example.com');
+
+// Login
+$loginResponse = $client->login(
+    'user@example.com', 
+    'password123'
+);
+
+// Create conversation
+$conversationResponse = $client->createConversation('My First Chat');
+
+// Send message
+$messageResponse = $client->sendMessage(
+    $conversationResponse->data['conversation']['id'],
+    'Hello, how can you help me today?'
+);
+
+echo "AI Response: " . $messageResponse->data['ai_response']['content'];
+
+ +
+
# Login
+curl -X POST https://api.example.com/auth/login \
+  -H "Content-Type: application/json" \
+  -d '{"email":"user@example.com","password":"password123"}'
+
+# Create conversation
+curl -X POST https://api.example.com/chat/conversations \
+  -H "Authorization: Bearer YOUR_TOKEN" \
+  -H "Content-Type: application/json" \
+  -d '{"title":"My First Chat"}'
+
+# Send message
+curl -X POST https://api.example.com/chat/conversations/1/messages \
+  -H "Authorization: Bearer YOUR_TOKEN" \
+  -H "Content-Type: application/json" \
+  -d '{"content":"Hello, how can you help me today?"}'
+
+
+
+ +
+

📋 Additional Resources

+
+
+

API Reference

+

Complete endpoint reference with examples and schemas.

+ View Reference → +
+ +
+

Database Schema

+

Database structure and relationships documentation.

+ View Schema → +
+ +
+

Architecture Guide

+

System architecture and design patterns.

+ View Architecture → +
+ +
+

Testing Guide

+

Testing procedures and integration tests.

+ View Testing → +
+ +
+

Contributing

+

Guidelines for contributing to the project.

+ View Guidelines → +
+ +
+

Changelog

+

Version history and release notes.

+ View Tasks → +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/openapi-generated.yaml b/docs/openapi-generated.yaml new file mode 100644 index 0000000..ceda9ea --- /dev/null +++ b/docs/openapi-generated.yaml @@ -0,0 +1,89 @@ +openapi: 3.0.0 +paths: + /auth/login: + post: + tags: + - Authentication + summary: 'User login' + description: 'Authenticate a user and return a JWT token' + operationId: b2ae86ccfb55be825ce2147231280f7d + requestBody: + required: true + content: + application/json: + schema: + required: + - email + - password + properties: + email: + type: string + format: email + example: user@example.com + password: + type: string + format: password + example: password123 + type: object + responses: + '200': + description: 'Login successful' + content: + application/json: + schema: + properties: + success: { type: boolean, example: true } + token: { type: string, example: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... } + user: { $ref: '#/components/schemas/User' } + type: object + '401': + description: Unauthorized + '422': + description: 'Validation Error' + /auth/register: + post: + tags: + - Authentication + summary: 'User registration' + description: 'Register a new user account' + operationId: 32c40a1dbdee45b17eda02025609b9c3 + requestBody: + required: true + content: + application/json: + schema: + required: + - name + - email + - password + properties: + name: + type: string + example: 'John Doe' + email: + type: string + format: email + example: user@example.com + password: + type: string + format: password + minLength: 8 + example: password123 + type: object + responses: + '201': + description: 'Registration successful' + content: + application/json: + schema: + properties: + success: { type: boolean, example: true } + message: { type: string, example: 'User registered successfully' } + user: { $ref: '#/components/schemas/User' } + type: object + '422': + description: 'Validation Error' +tags: + - + name: Authentication + description: 'User authentication and authorization' diff --git a/docs/openapi.json b/docs/openapi.json new file mode 100644 index 0000000..69559ae --- /dev/null +++ b/docs/openapi.json @@ -0,0 +1,148 @@ +{ + "openapi": "3.0.0", + "paths": { + "/auth/login": { + "post": { + "tags": [ + "Authentication" + ], + "summary": "User login", + "description": "Authenticate a user and return a JWT token", + "operationId": "b2ae86ccfb55be825ce2147231280f7d", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "required": [ + "email", + "password" + ], + "properties": { + "email": { + "type": "string", + "format": "email", + "example": "user@example.com" + }, + "password": { + "type": "string", + "format": "password", + "example": "password123" + } + }, + "type": "object" + } + } + } + }, + "responses": { + "200": { + "description": "Login successful", + "content": { + "application/json": { + "schema": { + "properties": { + "success": { + "type": "boolean", + "example": true + }, + "token": { + "type": "string", + "example": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." + }, + "user": { + "$ref": "#/components/schemas/User" + } + }, + "type": "object" + } + } + } + }, + "401": { + "description": "Unauthorized" + }, + "422": { + "description": "Validation Error" + } + } + } + }, + "/auth/register": { + "post": { + "tags": [ + "Authentication" + ], + "summary": "User registration", + "description": "Register a new user account", + "operationId": "32c40a1dbdee45b17eda02025609b9c3", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "required": [ + "name", + "email", + "password" + ], + "properties": { + "name": { + "type": "string", + "example": "John Doe" + }, + "email": { + "type": "string", + "format": "email", + "example": "user@example.com" + }, + "password": { + "type": "string", + "format": "password", + "minLength": 8, + "example": "password123" + } + }, + "type": "object" + } + } + } + }, + "responses": { + "201": { + "description": "Registration successful", + "content": { + "application/json": { + "schema": { + "properties": { + "success": { + "type": "boolean", + "example": true + }, + "message": { + "type": "string", + "example": "User registered successfully" + }, + "user": { + "$ref": "#/components/schemas/User" + } + }, + "type": "object" + } + } + } + }, + "422": { + "description": "Validation Error" + } + } + } + } + }, + "tags": [ + { + "name": "Authentication", + "description": "User authentication and authorization" + } + ] +} \ No newline at end of file diff --git a/docs/openapi.yaml b/docs/openapi.yaml new file mode 100644 index 0000000..5669582 --- /dev/null +++ b/docs/openapi.yaml @@ -0,0 +1,750 @@ +openapi: 3.0.3 +info: + title: AI Chat Application API + description: | + A comprehensive API for an AI chat application that supports real-time messaging, + conversation management, and user authentication. + + ## Features + - User authentication with JWT tokens + - Real-time chat functionality + - Conversation management + - File upload support + - WebSocket integration + + ## Authentication + This API uses JWT Bearer token authentication. Include the token in the Authorization header: + ``` + Authorization: Bearer + ``` + version: 1.0.0 + contact: + name: API Support + email: support@example.com + license: + name: MIT + url: https://opensource.org/licenses/MIT + +servers: + - url: http://localhost:8080 + description: Development server + - url: https://api.example.com + description: Production server + +security: + - bearerAuth: [] + +paths: + /auth/login: + post: + tags: + - Authentication + summary: User login + description: Authenticate a user and return a JWT token + operationId: login + security: [] + requestBody: + required: true + content: + application/json: + schema: + type: object + required: + - email + - password + properties: + email: + type: string + format: email + example: user@example.com + password: + type: string + format: password + example: password123 + examples: + valid_user: + summary: Valid user credentials + value: + email: user@example.com + password: password123 + responses: + '200': + description: Login successful + content: + application/json: + schema: + type: object + properties: + success: + type: boolean + example: true + token: + type: string + example: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... + user: + $ref: '#/components/schemas/User' + '401': + $ref: '#/components/responses/Unauthorized' + '422': + $ref: '#/components/responses/ValidationError' + + /auth/register: + post: + tags: + - Authentication + summary: User registration + description: Register a new user account + operationId: register + security: [] + requestBody: + required: true + content: + application/json: + schema: + type: object + required: + - name + - email + - password + properties: + name: + type: string + example: John Doe + email: + type: string + format: email + example: user@example.com + password: + type: string + format: password + minLength: 8 + example: password123 + responses: + '201': + description: Registration successful + content: + application/json: + schema: + type: object + properties: + success: + type: boolean + example: true + message: + type: string + example: User registered successfully + user: + $ref: '#/components/schemas/User' + '422': + $ref: '#/components/responses/ValidationError' + + /auth/logout: + post: + tags: + - Authentication + summary: User logout + description: Logout the current user and invalidate the JWT token + operationId: logout + responses: + '200': + description: Logout successful + content: + application/json: + schema: + type: object + properties: + success: + type: boolean + example: true + message: + type: string + example: Logged out successfully + '401': + $ref: '#/components/responses/Unauthorized' + + /auth/me: + get: + tags: + - Authentication + summary: Get current user + description: Get information about the currently authenticated user + operationId: getCurrentUser + responses: + '200': + description: User information retrieved successfully + content: + application/json: + schema: + type: object + properties: + success: + type: boolean + example: true + user: + $ref: '#/components/schemas/User' + '401': + $ref: '#/components/responses/Unauthorized' + + /chat/conversations: + get: + tags: + - Chat + summary: List conversations + description: Get all conversations for the authenticated user + operationId: getConversations + parameters: + - name: page + in: query + description: Page number for pagination + schema: + type: integer + minimum: 1 + default: 1 + - name: limit + in: query + description: Number of conversations per page + schema: + type: integer + minimum: 1 + maximum: 100 + default: 20 + responses: + '200': + description: Conversations retrieved successfully + content: + application/json: + schema: + type: object + properties: + success: + type: boolean + example: true + conversations: + type: array + items: + $ref: '#/components/schemas/Conversation' + pagination: + $ref: '#/components/schemas/Pagination' + '401': + $ref: '#/components/responses/Unauthorized' + + post: + tags: + - Chat + summary: Create conversation + description: Create a new conversation + operationId: createConversation + requestBody: + required: true + content: + application/json: + schema: + type: object + properties: + title: + type: string + example: "New Chat Session" + description: + type: string + example: "Discussion about AI capabilities" + responses: + '201': + description: Conversation created successfully + content: + application/json: + schema: + type: object + properties: + success: + type: boolean + example: true + conversation: + $ref: '#/components/schemas/Conversation' + '401': + $ref: '#/components/responses/Unauthorized' + '422': + $ref: '#/components/responses/ValidationError' + + /chat/conversations/{id}: + get: + tags: + - Chat + summary: Get conversation + description: Get a specific conversation with its messages + operationId: getConversation + parameters: + - name: id + in: path + required: true + description: Conversation ID + schema: + type: integer + example: 1 + responses: + '200': + description: Conversation retrieved successfully + content: + application/json: + schema: + type: object + properties: + success: + type: boolean + example: true + conversation: + $ref: '#/components/schemas/ConversationWithMessages' + '401': + $ref: '#/components/responses/Unauthorized' + '404': + $ref: '#/components/responses/NotFound' + + put: + tags: + - Chat + summary: Update conversation + description: Update conversation details + operationId: updateConversation + parameters: + - name: id + in: path + required: true + description: Conversation ID + schema: + type: integer + example: 1 + requestBody: + required: true + content: + application/json: + schema: + type: object + properties: + title: + type: string + example: "Updated Chat Title" + description: + type: string + example: "Updated description" + responses: + '200': + description: Conversation updated successfully + content: + application/json: + schema: + type: object + properties: + success: + type: boolean + example: true + conversation: + $ref: '#/components/schemas/Conversation' + '401': + $ref: '#/components/responses/Unauthorized' + '404': + $ref: '#/components/responses/NotFound' + '422': + $ref: '#/components/responses/ValidationError' + + delete: + tags: + - Chat + summary: Delete conversation + description: Delete a conversation and all its messages + operationId: deleteConversation + parameters: + - name: id + in: path + required: true + description: Conversation ID + schema: + type: integer + example: 1 + responses: + '200': + description: Conversation deleted successfully + content: + application/json: + schema: + type: object + properties: + success: + type: boolean + example: true + message: + type: string + example: "Conversation deleted successfully" + '401': + $ref: '#/components/responses/Unauthorized' + '404': + $ref: '#/components/responses/NotFound' + + /chat/conversations/{id}/messages: + get: + tags: + - Chat + summary: Get messages + description: Get messages for a specific conversation + operationId: getMessages + parameters: + - name: id + in: path + required: true + description: Conversation ID + schema: + type: integer + example: 1 + - name: page + in: query + description: Page number for pagination + schema: + type: integer + minimum: 1 + default: 1 + - name: limit + in: query + description: Number of messages per page + schema: + type: integer + minimum: 1 + maximum: 100 + default: 50 + responses: + '200': + description: Messages retrieved successfully + content: + application/json: + schema: + type: object + properties: + success: + type: boolean + example: true + messages: + type: array + items: + $ref: '#/components/schemas/Message' + pagination: + $ref: '#/components/schemas/Pagination' + '401': + $ref: '#/components/responses/Unauthorized' + '404': + $ref: '#/components/responses/NotFound' + + post: + tags: + - Chat + summary: Send message + description: Send a new message in a conversation + operationId: sendMessage + parameters: + - name: id + in: path + required: true + description: Conversation ID + schema: + type: integer + example: 1 + requestBody: + required: true + content: + application/json: + schema: + type: object + required: + - content + properties: + content: + type: string + example: "Hello, how can you help me today?" + attachments: + type: array + items: + type: string + example: ["file1.pdf", "image.jpg"] + examples: + text_message: + summary: Simple text message + value: + content: "Hello, how can you help me today?" + message_with_attachments: + summary: Message with file attachments + value: + content: "Please review these documents" + attachments: ["document.pdf", "chart.png"] + responses: + '201': + description: Message sent successfully + content: + application/json: + schema: + type: object + properties: + success: + type: boolean + example: true + message: + $ref: '#/components/schemas/Message' + ai_response: + $ref: '#/components/schemas/Message' + '401': + $ref: '#/components/responses/Unauthorized' + '404': + $ref: '#/components/responses/NotFound' + '422': + $ref: '#/components/responses/ValidationError' + + /chat/upload: + post: + tags: + - Chat + summary: Upload file + description: Upload a file for use in conversations + operationId: uploadFile + requestBody: + required: true + content: + multipart/form-data: + schema: + type: object + required: + - file + properties: + file: + type: string + format: binary + description: File to upload + conversation_id: + type: integer + description: Associated conversation ID + example: 1 + responses: + '201': + description: File uploaded successfully + content: + application/json: + schema: + type: object + properties: + success: + type: boolean + example: true + file: + $ref: '#/components/schemas/UploadedFile' + '401': + $ref: '#/components/responses/Unauthorized' + '422': + $ref: '#/components/responses/ValidationError' + + /chat/stream: + get: + tags: + - Chat + summary: WebSocket endpoint + description: WebSocket endpoint for real-time chat functionality + operationId: chatWebSocket + responses: + '101': + description: WebSocket connection established + '401': + $ref: '#/components/responses/Unauthorized' + +components: + securitySchemes: + bearerAuth: + type: http + scheme: bearer + bearerFormat: JWT + + schemas: + User: + type: object + properties: + id: + type: integer + example: 1 + name: + type: string + example: "John Doe" + email: + type: string + format: email + example: "user@example.com" + created_at: + type: string + format: date-time + example: "2024-01-15T10:30:00Z" + updated_at: + type: string + format: date-time + example: "2024-01-15T10:30:00Z" + + Conversation: + type: object + properties: + id: + type: integer + example: 1 + title: + type: string + example: "AI Discussion" + description: + type: string + example: "General AI conversation" + user_id: + type: integer + example: 1 + created_at: + type: string + format: date-time + example: "2024-01-15T10:30:00Z" + updated_at: + type: string + format: date-time + example: "2024-01-15T10:30:00Z" + message_count: + type: integer + example: 5 + + ConversationWithMessages: + allOf: + - $ref: '#/components/schemas/Conversation' + - type: object + properties: + messages: + type: array + items: + $ref: '#/components/schemas/Message' + + Message: + type: object + properties: + id: + type: integer + example: 1 + conversation_id: + type: integer + example: 1 + content: + type: string + example: "Hello, how can you help me?" + role: + type: string + enum: [user, assistant, system] + example: "user" + attachments: + type: array + items: + type: string + example: ["file1.pdf"] + created_at: + type: string + format: date-time + example: "2024-01-15T10:30:00Z" + updated_at: + type: string + format: date-time + example: "2024-01-15T10:30:00Z" + + UploadedFile: + type: object + properties: + id: + type: integer + example: 1 + filename: + type: string + example: "document.pdf" + original_name: + type: string + example: "my-document.pdf" + mime_type: + type: string + example: "application/pdf" + size: + type: integer + example: 1024576 + path: + type: string + example: "/uploads/documents/document.pdf" + created_at: + type: string + format: date-time + example: "2024-01-15T10:30:00Z" + + Pagination: + type: object + properties: + current_page: + type: integer + example: 1 + per_page: + type: integer + example: 20 + total: + type: integer + example: 100 + last_page: + type: integer + example: 5 + from: + type: integer + example: 1 + to: + type: integer + example: 20 + + Error: + type: object + properties: + success: + type: boolean + example: false + message: + type: string + example: "Error message" + errors: + type: object + additionalProperties: + type: array + items: + type: string + + responses: + Unauthorized: + description: Authentication required + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + example: + success: false + message: "Unauthorized access" + + NotFound: + description: Resource not found + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + example: + success: false + message: "Resource not found" + + ValidationError: + description: Validation error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + example: + success: false + message: "Validation failed" + errors: + email: ["The email field is required."] + password: ["The password must be at least 8 characters."] + +tags: + - name: Authentication + description: User authentication and authorization + - name: Chat + description: Chat functionality and conversation management \ No newline at end of file diff --git a/scripts/generate-docs.php b/scripts/generate-docs.php new file mode 100755 index 0000000..adbe04f --- /dev/null +++ b/scripts/generate-docs.php @@ -0,0 +1,307 @@ +#!/usr/bin/env php +basePath = dirname(__DIR__); + $this->outputPath = $this->basePath . '/docs'; + } + + /** + * Generate OpenAPI documentation + */ + public function generate(): void + { + echo "Generating OpenAPI documentation...\n"; + + // Define paths to scan for annotations + $scanPaths = [ + $this->basePath . '/app/Controllers', + $this->basePath . '/app/Models', + ]; + + // Generate OpenAPI documentation + $openapi = Generator::scan($scanPaths); + + // Write JSON specification + $jsonPath = $this->outputPath . '/openapi.json'; + file_put_contents($jsonPath, $openapi->toJson()); + echo "Generated JSON specification: $jsonPath\n"; + + // Write YAML specification + $yamlPath = $this->outputPath . '/openapi-generated.yaml'; + file_put_contents($yamlPath, $openapi->toYaml()); + echo "Generated YAML specification: $yamlPath\n"; + + // Generate interactive HTML documentation + $this->generateHtmlDocs(); + + echo "Documentation generation complete!\n"; + } + + /** + * Generate interactive HTML documentation + */ + private function generateHtmlDocs(): void + { + $htmlContent = $this->generateSwaggerUI(); + $htmlPath = $this->outputPath . '/api-docs.html'; + file_put_contents($htmlPath, $htmlContent); + echo "Generated interactive HTML documentation: $htmlPath\n"; + } + + /** + * Generate Swagger UI HTML + */ + private function generateSwaggerUI(): string + { + return << + + + + + AI Chat Application API Documentation + + + + +
+

🤖 AI Chat Application API

+

Interactive API Documentation

+
+ +
+
+
+

📋 API Features

+
    +
  • User Authentication & Authorization
  • +
  • Real-time Chat Messaging
  • +
  • Conversation Management
  • +
  • File Upload Support
  • +
  • WebSocket Integration
  • +
  • Pagination & Filtering
  • +
+
+ +
+

🔐 Authentication

+

This API uses JWT Bearer token authentication. Include the token in your requests:

+
+ Authorization: Bearer YOUR_JWT_TOKEN +
+
+ +
+

🌐 Base URLs

+

Development: http://localhost:8080

+

Production: https://api.example.com

+
+ +
+

📚 Getting Started

+
    +
  1. Register a new user account
  2. +
  3. Login to get your JWT token
  4. +
  5. Use the token to access protected endpoints
  6. +
  7. Start creating conversations and sending messages
  8. +
+
+
+
+ +
+ + + + + + +HTML; + } + + /** + * Generate Redoc HTML documentation + */ + public function generateRedocDocs(): void + { + $htmlContent = << + + + AI Chat Application API - Redoc + + + + + + +
+

🤖 AI Chat Application API

+

Comprehensive API Documentation

+
+ + + + +HTML; + + $htmlPath = $this->outputPath . '/api-docs-redoc.html'; + file_put_contents($htmlPath, $htmlContent); + echo "Generated Redoc documentation: $htmlPath\n"; + } +} + +// Run the documentation generator +try { + $generator = new DocumentationGenerator(); + $generator->generate(); + $generator->generateRedocDocs(); +} catch (Exception $e) { + echo "Error generating documentation: " . $e->getMessage() . "\n"; + exit(1); +} \ No newline at end of file