Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
101 changes: 101 additions & 0 deletions app/Config/OpenApi.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
<?php

namespace App\Config;

use OpenApi\Attributes as OA;

/**
* OpenAPI Configuration and Global Schemas
*/
#[OA\Info(
version: "1.0.0",
title: "AI Chat Application API",
description: "A comprehensive API for an AI chat application that supports real-time messaging, conversation management, and user authentication."
)]
#[OA\Server(
url: "http://localhost:8080",
description: "Development server"
)]
#[OA\Server(
url: "https://api.example.com",
description: "Production server"
)]
#[OA\SecurityScheme(
securityScheme: "bearerAuth",
type: "http",
scheme: "bearer",
bearerFormat: "JWT"
)]
class OpenApi
{
// Schema definitions
}

/**
* User Schema
*/
#[OA\Schema(
schema: "User",
properties: [
new OA\Property(property: "id", type: "integer", example: 1),
new OA\Property(property: "name", type: "string", example: "John Doe"),
new OA\Property(property: "email", type: "string", format: "email", example: "[email protected]"),
new OA\Property(property: "created_at", type: "string", format: "date-time", example: "2024-01-15T10:30:00Z"),
new OA\Property(property: "updated_at", type: "string", format: "date-time", example: "2024-01-15T10:30:00Z")
]
)]
class UserSchema {}

/**
* Conversation Schema
*/
#[OA\Schema(
schema: "Conversation",
properties: [
new OA\Property(property: "id", type: "integer", example: 1),
new OA\Property(property: "title", type: "string", example: "AI Discussion"),
new OA\Property(property: "description", type: "string", example: "General AI conversation"),
new OA\Property(property: "user_id", type: "integer", example: 1),
new OA\Property(property: "created_at", type: "string", format: "date-time", example: "2024-01-15T10:30:00Z"),
new OA\Property(property: "updated_at", type: "string", format: "date-time", example: "2024-01-15T10:30:00Z"),
new OA\Property(property: "message_count", type: "integer", example: 5)
]
)]
class ConversationSchema {}

/**
* Message Schema
*/
#[OA\Schema(
schema: "Message",
properties: [
new OA\Property(property: "id", type: "integer", example: 1),
new OA\Property(property: "conversation_id", type: "integer", example: 1),
new OA\Property(property: "content", type: "string", example: "Hello, how can you help me?"),
new OA\Property(property: "role", type: "string", enum: ["user", "assistant", "system"], example: "user"),
new OA\Property(property: "attachments", type: "array", items: new OA\Items(type: "string"), example: ["file1.pdf"]),
new OA\Property(property: "created_at", type: "string", format: "date-time", example: "2024-01-15T10:30:00Z"),
new OA\Property(property: "updated_at", type: "string", format: "date-time", example: "2024-01-15T10:30:00Z")
]
)]
class MessageSchema {}

/**
* Error Schema
*/
#[OA\Schema(
schema: "Error",
properties: [
new OA\Property(property: "success", type: "boolean", example: false),
new OA\Property(property: "message", type: "string", example: "Error message"),
new OA\Property(
property: "errors",
type: "object",
additionalProperties: new OA\AdditionalProperties(
type: "array",
items: new OA\Items(type: "string")
)
)
]
)]
class ErrorSchema {}
175 changes: 174 additions & 1 deletion app/Controllers/Auth.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@
use App\Models\UserModel;
use App\Helpers\UserHelper;
use CodeIgniter\I18n\Time;
use OpenApi\Attributes as OA;

/**
* Auth Controller
*
* Handles user authentication including registration, login, and logout
*/
#[OA\Tag(name: "Authentication", description: "User authentication and authorization")]
class Auth extends BaseController
{
/**
Expand All @@ -29,7 +31,7 @@ class Auth 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->userModel = new UserModel();
Expand Down Expand Up @@ -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: "[email protected]"),
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: "[email protected]"),
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');
}
}
}
2 changes: 1 addition & 1 deletion app/Controllers/Chat.php
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": {
Expand Down
Loading