add api spec and mock api server
This commit is contained in:
		
							
								
								
									
										2
									
								
								src/api/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								src/api/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,2 @@ | ||||
| node_modules | ||||
| package-lock.json | ||||
							
								
								
									
										157
									
								
								src/api/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										157
									
								
								src/api/README.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,157 @@ | ||||
| # Freezone Backend API | ||||
|  | ||||
| A comprehensive API for managing digital residents, free zone companies, invoices, and expenses. Designed for authorized resellers to register and manage entities in the freezone system. | ||||
|  | ||||
| ## Quick Start | ||||
|  | ||||
| ### 1. Authentication | ||||
| ```bash | ||||
| curl -X POST https://api.freezone.com/v1/auth/api-key \ | ||||
|   -H "Content-Type: application/json" \ | ||||
|   -d '{ | ||||
|     "client_id": "your_client_id", | ||||
|     "client_secret": "your_client_secret",  | ||||
|     "access_password": "freezone_access_password" | ||||
|   }' | ||||
| ``` | ||||
|  | ||||
| ### 2. Use API Key | ||||
| ```bash | ||||
| curl -H "X-API-Key: your_api_key" https://api.freezone.com/v1/digital-residents | ||||
| ``` | ||||
|  | ||||
| ## Key Concepts | ||||
|  | ||||
| ### Dual ID System | ||||
| - **Input**: Provide your reseller IDs when creating entities | ||||
| - **Output**: Receive Freezone-issued IDs for all operations | ||||
| - **Operations**: Use Freezone IDs for all subsequent CRUD operations | ||||
|  | ||||
| | Entity | Reseller ID (Input) | Freezone ID (Operations) | | ||||
| |--------|-------------------|-------------------------| | ||||
| | Digital Resident | `reseller_user_id` | `resident_id` | | ||||
| | Free Zone Company | `reseller_company_id` | `fzc_id` | | ||||
| | Invoice | `reseller_invoice_id` | `fz_invoice_id` | | ||||
| | Expense | `reseller_expense_id` | `fz_expense_id` | | ||||
|  | ||||
| ### Prerequisites & Dependencies | ||||
|  | ||||
| #### Creating Free Zone Companies | ||||
| - **Required**: All `shareholder_resident_ids` must be registered digital residents | ||||
| - **Validation**: API validates that all provided resident IDs exist before company creation | ||||
| - **Example**: To create a company with shareholders `["fz_resident_abc123", "fz_resident_def456"]`, both residents must exist | ||||
|  | ||||
| #### Creating Invoices/Expenses | ||||
| - **Required**: Valid `fzc_id` (company must exist) | ||||
| - **Scope**: All invoices and expenses are company-scoped | ||||
|  | ||||
| ## API Endpoints | ||||
|  | ||||
| ### Digital Residents | ||||
| ``` | ||||
| GET    /digital-residents                    # List residents | ||||
| POST   /digital-residents                    # Create (provide reseller_user_id) | ||||
| GET    /digital-residents/{resident_id}      # Get by Freezone ID | ||||
| PUT    /digital-residents/{resident_id}      # Update by Freezone ID | ||||
| DELETE /digital-residents/{resident_id}      # Delete by Freezone ID | ||||
| ``` | ||||
|  | ||||
| ### Free Zone Companies | ||||
| ``` | ||||
| GET    /free-zone-companies                  # List companies | ||||
| POST   /free-zone-companies                  # Create (provide reseller_company_id) | ||||
| GET    /free-zone-companies/{fzc_id}         # Get by Freezone ID | ||||
| PUT    /free-zone-companies/{fzc_id}         # Update by Freezone ID | ||||
| DELETE /free-zone-companies/{fzc_id}         # Delete by Freezone ID | ||||
| ``` | ||||
|  | ||||
| ### Invoices (Company-scoped) | ||||
| ``` | ||||
| GET    /free-zone-companies/{fzc_id}/invoices                    # List invoices | ||||
| POST   /free-zone-companies/{fzc_id}/invoices                    # Create invoice | ||||
| GET    /free-zone-companies/{fzc_id}/invoices/{fz_invoice_id}    # Get invoice | ||||
| PUT    /free-zone-companies/{fzc_id}/invoices/{fz_invoice_id}    # Update invoice | ||||
| DELETE /free-zone-companies/{fzc_id}/invoices/{fz_invoice_id}    # Delete invoice | ||||
| ``` | ||||
|  | ||||
| ### Expenses (Company-scoped) | ||||
| ``` | ||||
| GET    /free-zone-companies/{fzc_id}/expenses                    # List expenses | ||||
| POST   /free-zone-companies/{fzc_id}/expenses                    # Create expense | ||||
| GET    /free-zone-companies/{fzc_id}/expenses/{fz_expense_id}    # Get expense | ||||
| PUT    /free-zone-companies/{fzc_id}/expenses/{fz_expense_id}    # Update expense | ||||
| DELETE /free-zone-companies/{fzc_id}/expenses/{fz_expense_id}    # Delete expense | ||||
| ``` | ||||
|  | ||||
| ## Example Workflow | ||||
|  | ||||
| ### 1. Create Digital Residents | ||||
| ```json | ||||
| POST /digital-residents | ||||
| { | ||||
|   "reseller_user_id": "reseller_user_123", | ||||
|   "email": "john@example.com", | ||||
|   "first_name": "John", | ||||
|   "last_name": "Doe", | ||||
|   "kyc_provider_id": "kyc_john_789" | ||||
| } | ||||
| // Returns: resident_id: "fz_resident_abc123" | ||||
| ``` | ||||
|  | ||||
| ### 2. Create Free Zone Company | ||||
| ```json | ||||
| POST /free-zone-companies | ||||
| { | ||||
|   "reseller_company_id": "reseller_comp_456", | ||||
|   "name": "Tech Innovations FZC", | ||||
|   "type": "company", | ||||
|   "shareholder_resident_ids": ["fz_resident_abc123"], // Must exist! | ||||
|   "crypto_wallets": [ | ||||
|     { | ||||
|       "public_key": "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa", | ||||
|       "chain": "bitcoin", | ||||
|       "label": "Main Bitcoin Wallet" | ||||
|     } | ||||
|   ] | ||||
| } | ||||
| // Returns: fzc_id: "fz_company_xyz789" | ||||
| ``` | ||||
|  | ||||
| ### 3. Create Invoice | ||||
| ```json | ||||
| POST /free-zone-companies/fz_company_xyz789/invoices | ||||
| { | ||||
|   "reseller_invoice_id": "reseller_inv_789", | ||||
|   "invoice_number": "INV-2024-001", | ||||
|   "amount": 1500.00, | ||||
|   "currency": "USD", | ||||
|   "issue_date": "2024-01-15" | ||||
| } | ||||
| // Returns: fz_invoice_id: "fz_invoice_inv123" | ||||
| ``` | ||||
|  | ||||
| ## Rate Limiting | ||||
| - **Limit**: 100 requests per minute | ||||
| - **Headers**: Check `X-RateLimit-Remaining` and `X-RateLimit-Reset` | ||||
|  | ||||
| ## Error Handling | ||||
| - **400**: Bad request (validation errors) | ||||
| - **401**: Invalid/missing API key | ||||
| - **404**: Resource not found | ||||
| - **409**: Conflict (duplicate reseller IDs) | ||||
| - **429**: Rate limit exceeded | ||||
|  | ||||
| ## KYC Integration | ||||
| Use the `kyc_provider_id` from digital residents to query your KYC provider for verification data. | ||||
|  | ||||
| ## Crypto Wallet Support | ||||
| Supported chains: `bitcoin`, `ethereum`, `polygon`, `binance_smart_chain`, `solana` | ||||
|  | ||||
| ## Documentation | ||||
| - **Interactive API Docs**: [Swagger UI](./swagger-ui.html) | ||||
| - **OpenAPI Spec**: [openapi_updated.yaml](./openapi_updated.yaml) | ||||
|  | ||||
| ## Support | ||||
| - **Email**: api-support@freezone.com | ||||
| - **Rate Limiting**: 100 requests/minute | ||||
| - **Authentication**: API key required for all endpoints | ||||
							
								
								
									
										1743
									
								
								src/api/openapi.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1743
									
								
								src/api/openapi.yaml
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										2032
									
								
								src/api/openapi_updated.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2032
									
								
								src/api/openapi_updated.yaml
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										30
									
								
								src/api/package.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								src/api/package.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,30 @@ | ||||
| { | ||||
|   "name": "freezone-api-docs", | ||||
|   "version": "1.0.0", | ||||
|   "description": "Documentation and mock server for Freezone API", | ||||
|   "main": "server.js", | ||||
|   "scripts": { | ||||
|     "start": "node server.js", | ||||
|     "dev": "nodemon server.js", | ||||
|     "install-deps": "npm install", | ||||
|     "test-mock": "./test-mock-api.sh" | ||||
|   }, | ||||
|   "dependencies": { | ||||
|     "express": "^4.18.2", | ||||
|     "http-proxy-middleware": "^2.0.6", | ||||
|     "@stoplight/prism-cli": "^5.8.0" | ||||
|   }, | ||||
|   "devDependencies": { | ||||
|     "nodemon": "^3.0.2" | ||||
|   }, | ||||
|   "keywords": [ | ||||
|     "openapi", | ||||
|     "mock", | ||||
|     "api", | ||||
|     "freezone", | ||||
|     "swagger", | ||||
|     "documentation" | ||||
|   ], | ||||
|   "author": "Freezone API Team", | ||||
|   "license": "MIT" | ||||
| } | ||||
							
								
								
									
										40
									
								
								src/api/serve.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										40
									
								
								src/api/serve.sh
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,40 @@ | ||||
| #!/bin/bash | ||||
|  | ||||
| # Simple HTTP server to serve the Swagger UI and OpenAPI spec | ||||
| # Usage: ./serve.sh [port] | ||||
|  | ||||
| PORT=${1:-8080} | ||||
| API_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" | ||||
|  | ||||
| echo "🚀 Starting Freezone API Documentation Server..." | ||||
| echo "📁 Serving from: $API_DIR" | ||||
| echo "🌐 URL: http://localhost:$PORT" | ||||
| echo "📖 Swagger UI: http://localhost:$PORT/swagger-ui.html" | ||||
| echo "" | ||||
| echo "📋 Available files:" | ||||
| echo "   - swagger-ui.html (Interactive API documentation)" | ||||
| echo "   - openapi_updated.yaml (OpenAPI specification)" | ||||
| echo "   - README.md (API documentation)" | ||||
| echo "" | ||||
| echo "Press Ctrl+C to stop the server" | ||||
| echo "----------------------------------------" | ||||
|  | ||||
| # Check if Python 3 is available | ||||
| if command -v python3 &> /dev/null; then | ||||
|     cd "$API_DIR" | ||||
|     python3 -m http.server $PORT | ||||
| elif command -v python &> /dev/null; then | ||||
|     cd "$API_DIR" | ||||
|     python -m http.server $PORT | ||||
| elif command -v node &> /dev/null; then | ||||
|     # Use Node.js if available | ||||
|     cd "$API_DIR" | ||||
|     npx http-server -p $PORT | ||||
| else | ||||
|     echo "❌ Error: No suitable HTTP server found." | ||||
|     echo "Please install one of the following:" | ||||
|     echo "   - Python 3: python3 -m http.server" | ||||
|     echo "   - Python 2: python -m SimpleHTTPServer" | ||||
|     echo "   - Node.js: npx http-server" | ||||
|     exit 1 | ||||
| fi | ||||
							
								
								
									
										175
									
								
								src/api/server.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										175
									
								
								src/api/server.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,175 @@ | ||||
| const express = require('express'); | ||||
| const path = require('path'); | ||||
| const { createProxyMiddleware } = require('http-proxy-middleware'); | ||||
| const { spawn } = require('child_process'); | ||||
|  | ||||
| const app = express(); | ||||
| const PORT = process.env.PORT || 3000; | ||||
|  | ||||
| // Serve static files (documentation) | ||||
| app.use(express.static(__dirname)); | ||||
|  | ||||
| // Start Prism mock server on a different port internally | ||||
| let prismProcess; | ||||
|  | ||||
| function startPrismServer() { | ||||
|     console.log('🔧 Starting internal mock server...'); | ||||
|     prismProcess = spawn('npx', ['prism', 'mock', 'openapi_updated.yaml', '--host', '127.0.0.1', '--port', '4001', '--dynamic'], { | ||||
|         cwd: __dirname, | ||||
|         stdio: ['pipe', 'pipe', 'pipe'] | ||||
|     }); | ||||
|  | ||||
|     prismProcess.stdout.on('data', (data) => { | ||||
|         const output = data.toString().trim(); | ||||
|         if (output && !output.includes('Prism is listening')) { | ||||
|             console.log(`[Internal] ${output}`); | ||||
|         } | ||||
|     }); | ||||
|  | ||||
|     prismProcess.stderr.on('data', (data) => { | ||||
|         const output = data.toString().trim(); | ||||
|         if (output) { | ||||
|             console.log(`[Internal] ${output}`); | ||||
|         } | ||||
|     }); | ||||
|  | ||||
|     prismProcess.on('close', (code) => { | ||||
|         console.log(`[Internal] Mock server process exited with code ${code}`); | ||||
|     }); | ||||
|  | ||||
|     // Give Prism time to start | ||||
|     setTimeout(() => { | ||||
|         console.log('✅ Mock API service ready at /api/mock'); | ||||
|     }, 3000); | ||||
| } | ||||
|  | ||||
| // Proxy /api/mock/* to the internal Prism server | ||||
| app.use('/api/mock', createProxyMiddleware({ | ||||
|     target: 'http://127.0.0.1:4001', | ||||
|     changeOrigin: true, | ||||
|     pathRewrite: { | ||||
|         '^/api/mock': '', // Remove /api/mock prefix when forwarding to Prism | ||||
|     }, | ||||
|     onError: (err, req, res) => { | ||||
|         console.error('Proxy error:', err.message); | ||||
|         res.status(503).json({ | ||||
|             error: 'mock_server_unavailable', | ||||
|             message: 'Mock API server is not available. Please wait a moment and try again.', | ||||
|             details: { | ||||
|                 endpoint: req.originalUrl, | ||||
|                 internal_error: err.message | ||||
|             } | ||||
|         }); | ||||
|     }, | ||||
|     onProxyReq: (proxyReq, req, res) => { | ||||
|         console.log(`[Mock API] ${req.method} ${req.originalUrl} -> http://127.0.0.1:4001${req.url}`); | ||||
|     } | ||||
| })); | ||||
|  | ||||
| // API status endpoint | ||||
| app.get('/api/status', (req, res) => { | ||||
|     res.json({ | ||||
|         status: 'running', | ||||
|         services: { | ||||
|             documentation: { | ||||
|                 status: 'active', | ||||
|                 endpoints: [ | ||||
|                     'GET /', | ||||
|                     'GET /swagger-ui.html', | ||||
|                     'GET /openapi_updated.yaml', | ||||
|                     'GET /README.md' | ||||
|                 ] | ||||
|             }, | ||||
|             mock_api: { | ||||
|                 status: prismProcess ? 'active' : 'starting', | ||||
|                 base_url: '/api/mock', | ||||
|                 endpoints: [ | ||||
|                     'POST /api/mock/auth/api-key', | ||||
|                     'GET /api/mock/digital-residents', | ||||
|                     'POST /api/mock/digital-residents', | ||||
|                     'GET /api/mock/free-zone-companies', | ||||
|                     'POST /api/mock/free-zone-companies', | ||||
|                     'GET /api/mock/free-zone-companies/{fzc_id}/invoices', | ||||
|                     'GET /api/mock/free-zone-companies/{fzc_id}/expenses' | ||||
|                 ] | ||||
|             } | ||||
|         }, | ||||
|         timestamp: new Date().toISOString() | ||||
|     }); | ||||
| }); | ||||
|  | ||||
| // Health check endpoint | ||||
| app.get('/health', (req, res) => { | ||||
|     res.json({ status: 'healthy', timestamp: new Date().toISOString() }); | ||||
| }); | ||||
|  | ||||
| // Default route - redirect to Swagger UI | ||||
| app.get('/', (req, res) => { | ||||
|     res.redirect('/swagger-ui.html'); | ||||
| }); | ||||
|  | ||||
| // 404 handler | ||||
| app.use((req, res) => { | ||||
|     res.status(404).json({ | ||||
|         error: 'not_found', | ||||
|         message: `Endpoint ${req.method} ${req.path} not found`, | ||||
|         available_endpoints: { | ||||
|             documentation: [ | ||||
|                 'GET /', | ||||
|                 'GET /swagger-ui.html', | ||||
|                 'GET /openapi_updated.yaml', | ||||
|                 'GET /README.md' | ||||
|             ], | ||||
|             mock_api: [ | ||||
|                 'POST /api/mock/auth/api-key', | ||||
|                 'GET /api/mock/digital-residents', | ||||
|                 'POST /api/mock/digital-residents', | ||||
|                 'GET /api/mock/free-zone-companies', | ||||
|                 'POST /api/mock/free-zone-companies' | ||||
|             ], | ||||
|             status: [ | ||||
|                 'GET /api/status', | ||||
|                 'GET /health' | ||||
|             ] | ||||
|         } | ||||
|     }); | ||||
| }); | ||||
|  | ||||
| // Graceful shutdown | ||||
| process.on('SIGINT', () => { | ||||
|     console.log('\n🛑 Shutting down servers...'); | ||||
|     if (prismProcess) { | ||||
|         prismProcess.kill(); | ||||
|     } | ||||
|     process.exit(0); | ||||
| }); | ||||
|  | ||||
| // Start the server | ||||
| app.listen(PORT, () => { | ||||
|     console.log('🚀 Freezone API Documentation & Mock Server'); | ||||
|     console.log('=========================================='); | ||||
|     console.log(`🌐 Server running on: http://localhost:${PORT}`); | ||||
|     console.log(''); | ||||
|     console.log('📋 Available Services:'); | ||||
|     console.log(`   📖 Documentation: http://localhost:${PORT}/swagger-ui.html`); | ||||
|     console.log(`   🔧 Mock API: http://localhost:${PORT}/api/mock`); | ||||
|     console.log(`   📄 OpenAPI Spec: http://localhost:${PORT}/openapi_updated.yaml`); | ||||
|     console.log(`   📊 Status: http://localhost:${PORT}/api/status`); | ||||
|     console.log(''); | ||||
|     console.log('🔧 Mock API Examples:'); | ||||
|     console.log(`   POST http://localhost:${PORT}/api/mock/auth/api-key`); | ||||
|     console.log(`   GET  http://localhost:${PORT}/api/mock/digital-residents`); | ||||
|     console.log(`   POST http://localhost:${PORT}/api/mock/digital-residents`); | ||||
|     console.log(`   GET  http://localhost:${PORT}/api/mock/free-zone-companies`); | ||||
|     console.log(''); | ||||
|     console.log('💡 Tips:'); | ||||
|     console.log('   - Mock API returns example responses from OpenAPI spec'); | ||||
|     console.log('   - Use X-API-Key header for authenticated endpoints'); | ||||
|     console.log('   - All services run on the same port for convenience'); | ||||
|     console.log(''); | ||||
|     console.log('Press Ctrl+C to stop the server'); | ||||
|     console.log('=========================================='); | ||||
|      | ||||
|     // Start Prism after Express server is running | ||||
|     startPrismServer(); | ||||
| }); | ||||
							
								
								
									
										75
									
								
								src/api/start-mock-server.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										75
									
								
								src/api/start-mock-server.sh
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,75 @@ | ||||
| #!/bin/bash | ||||
|  | ||||
| # Start both documentation and mock API server | ||||
| # Usage: ./start-mock-server.sh | ||||
|  | ||||
| API_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" | ||||
| cd "$API_DIR" | ||||
|  | ||||
| echo "🚀 Starting Freezone API Mock Server & Documentation..." | ||||
| echo "" | ||||
| echo "📋 Services:" | ||||
| echo "   📖 Documentation: http://localhost:3000/swagger-ui.html" | ||||
| echo "   🔧 Mock API: http://localhost:4000" | ||||
| echo "   📄 OpenAPI Spec: http://localhost:3000/openapi_updated.yaml" | ||||
| echo "" | ||||
| echo "🔧 Mock API Endpoints:" | ||||
| echo "   POST http://localhost:4000/auth/api-key" | ||||
| echo "   GET  http://localhost:4000/digital-residents" | ||||
| echo "   POST http://localhost:4000/digital-residents" | ||||
| echo "   GET  http://localhost:4000/free-zone-companies" | ||||
| echo "   POST http://localhost:4000/free-zone-companies" | ||||
| echo "   GET  http://localhost:4000/free-zone-companies/{fzc_id}/invoices" | ||||
| echo "   GET  http://localhost:4000/free-zone-companies/{fzc_id}/expenses" | ||||
| echo "" | ||||
| echo "💡 Tips:" | ||||
| echo "   - Mock API returns example responses from OpenAPI spec" | ||||
| echo "   - Use 'X-API-Key: test-key' header for authenticated endpoints" | ||||
| echo "   - Dynamic responses include realistic data variations" | ||||
| echo "" | ||||
| echo "Press Ctrl+C to stop all servers" | ||||
| echo "----------------------------------------" | ||||
|  | ||||
| # Check if Node.js and npm are available | ||||
| if ! command -v node &> /dev/null; then | ||||
|     echo "❌ Error: Node.js is required but not installed." | ||||
|     echo "Please install Node.js from https://nodejs.org/" | ||||
|     exit 1 | ||||
| fi | ||||
|  | ||||
| if ! command -v npm &> /dev/null; then | ||||
|     echo "❌ Error: npm is required but not installed." | ||||
|     echo "Please install npm (usually comes with Node.js)" | ||||
|     exit 1 | ||||
| fi | ||||
|  | ||||
| # Check if Prism is installed globally | ||||
| if ! command -v prism &> /dev/null; then | ||||
|     echo "📦 Installing Prism CLI globally..." | ||||
|     npm install -g @stoplight/prism-cli | ||||
|     if [ $? -ne 0 ]; then | ||||
|         echo "❌ Failed to install Prism CLI. Trying local installation..." | ||||
|         npm install @stoplight/prism-cli | ||||
|         PRISM_CMD="npx prism" | ||||
|     else | ||||
|         PRISM_CMD="prism" | ||||
|     fi | ||||
| else | ||||
|     PRISM_CMD="prism" | ||||
| fi | ||||
|  | ||||
| # Check if concurrently is available | ||||
| if ! command -v concurrently &> /dev/null; then | ||||
|     echo "📦 Installing concurrently..." | ||||
|     npm install concurrently | ||||
|     CONCURRENTLY_CMD="npx concurrently" | ||||
| else | ||||
|     CONCURRENTLY_CMD="concurrently" | ||||
| fi | ||||
|  | ||||
| # Start both servers concurrently | ||||
| $CONCURRENTLY_CMD \ | ||||
|     --names "DOCS,MOCK" \ | ||||
|     --prefix-colors "blue,green" \ | ||||
|     "python3 -m http.server 3000" \ | ||||
|     "$PRISM_CMD mock openapi_updated.yaml --host 0.0.0.0 --port 4000 --dynamic" | ||||
							
								
								
									
										153
									
								
								src/api/swagger-ui.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										153
									
								
								src/api/swagger-ui.html
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,153 @@ | ||||
| <!DOCTYPE html> | ||||
| <html lang="en"> | ||||
| <head> | ||||
|     <meta charset="UTF-8"> | ||||
|     <meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||||
|     <title>Freezone API Documentation</title> | ||||
|     <link rel="stylesheet" type="text/css" href="https://unpkg.com/swagger-ui-dist@5.10.5/swagger-ui.css" /> | ||||
|     <style> | ||||
|         html { | ||||
|             box-sizing: border-box; | ||||
|             overflow: -moz-scrollbars-vertical; | ||||
|             overflow-y: scroll; | ||||
|         } | ||||
|         *, *:before, *:after { | ||||
|             box-sizing: inherit; | ||||
|         } | ||||
|         body { | ||||
|             margin: 0; | ||||
|             background: #fafafa; | ||||
|         } | ||||
|         .swagger-ui .topbar { | ||||
|             background-color: #1f4e79; | ||||
|         } | ||||
|         .swagger-ui .topbar .download-url-wrapper .select-label { | ||||
|             color: #fff; | ||||
|         } | ||||
|         .swagger-ui .topbar .download-url-wrapper input[type=text] { | ||||
|             border: 2px solid #547ca3; | ||||
|         } | ||||
|         .swagger-ui .info .title { | ||||
|             color: #1f4e79; | ||||
|         } | ||||
|         .custom-header { | ||||
|             background: linear-gradient(135deg, #1f4e79 0%, #2980b9 100%); | ||||
|             color: white; | ||||
|             padding: 20px; | ||||
|             text-align: center; | ||||
|             margin-bottom: 20px; | ||||
|         } | ||||
|         .custom-header h1 { | ||||
|             margin: 0; | ||||
|             font-size: 2.5em; | ||||
|             font-weight: 300; | ||||
|         } | ||||
|         .custom-header p { | ||||
|             margin: 10px 0 0 0; | ||||
|             font-size: 1.2em; | ||||
|             opacity: 0.9; | ||||
|         } | ||||
|         .api-info { | ||||
|             background: white; | ||||
|             padding: 20px; | ||||
|             margin: 20px; | ||||
|             border-radius: 8px; | ||||
|             box-shadow: 0 2px 4px rgba(0,0,0,0.1); | ||||
|         } | ||||
|         .api-info h3 { | ||||
|             color: #1f4e79; | ||||
|             margin-top: 0; | ||||
|         } | ||||
|         .api-info .highlight { | ||||
|             background: #e8f4fd; | ||||
|             padding: 15px; | ||||
|             border-left: 4px solid #2980b9; | ||||
|             margin: 15px 0; | ||||
|         } | ||||
|         .api-info code { | ||||
|             background: #f8f9fa; | ||||
|             padding: 2px 6px; | ||||
|             border-radius: 3px; | ||||
|             font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace; | ||||
|         } | ||||
|     </style> | ||||
| </head> | ||||
| <body> | ||||
|     <div id="swagger-ui"></div> | ||||
|  | ||||
|     <script src="https://unpkg.com/swagger-ui-dist@5.10.5/swagger-ui-bundle.js"></script> | ||||
|     <script src="https://unpkg.com/swagger-ui-dist@5.10.5/swagger-ui-standalone-preset.js"></script> | ||||
|     <script> | ||||
|         window.onload = function() { | ||||
|             // Begin Swagger UI call region | ||||
|             const ui = SwaggerUIBundle({ | ||||
|                 url: './openapi_updated.yaml', | ||||
|                 dom_id: '#swagger-ui', | ||||
|                 deepLinking: true, | ||||
|                 presets: [ | ||||
|                     SwaggerUIBundle.presets.apis, | ||||
|                     SwaggerUIStandalonePreset | ||||
|                 ], | ||||
|                 plugins: [ | ||||
|                     SwaggerUIBundle.plugins.DownloadUrl | ||||
|                 ], | ||||
|                 layout: "StandaloneLayout", | ||||
|                 tryItOutEnabled: true, | ||||
|                 requestInterceptor: function(request) { | ||||
|                     // Add API key header if available | ||||
|                     const apiKey = localStorage.getItem('freezone_api_key'); | ||||
|                     if (apiKey) { | ||||
|                         request.headers['X-API-Key'] = apiKey; | ||||
|                     } | ||||
|                     return request; | ||||
|                 }, | ||||
|                 onComplete: function() { | ||||
|                     // Add custom functionality after Swagger UI loads | ||||
|                     console.log('Freezone API Documentation loaded'); | ||||
|                      | ||||
|                     // Add API key input functionality | ||||
|                     const topbar = document.querySelector('.topbar'); | ||||
|                     if (topbar) { | ||||
|                         const apiKeyInput = document.createElement('div'); | ||||
|                         apiKeyInput.innerHTML = ` | ||||
|                             <div style="display: inline-block; margin-left: 20px;"> | ||||
|                                 <label style="color: white; margin-right: 10px;">API Key:</label> | ||||
|                                 <input type="password" id="api-key-input" placeholder="Enter your API key"  | ||||
|                                        style="padding: 5px; border-radius: 3px; border: 1px solid #ccc; width: 200px;"> | ||||
|                                 <button onclick="setApiKey()" style="margin-left: 5px; padding: 5px 10px; background: #4CAF50; color: white; border: none; border-radius: 3px; cursor: pointer;">Set</button> | ||||
|                                 <button onclick="clearApiKey()" style="margin-left: 5px; padding: 5px 10px; background: #f44336; color: white; border: none; border-radius: 3px; cursor: pointer;">Clear</button> | ||||
|                             </div> | ||||
|                         `; | ||||
|                         topbar.appendChild(apiKeyInput); | ||||
|                          | ||||
|                         // Load saved API key | ||||
|                         const savedKey = localStorage.getItem('freezone_api_key'); | ||||
|                         if (savedKey) { | ||||
|                             document.getElementById('api-key-input').value = savedKey; | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             }); | ||||
|             // End Swagger UI call region | ||||
|  | ||||
|             window.ui = ui; | ||||
|         }; | ||||
|  | ||||
|         function setApiKey() { | ||||
|             const apiKey = document.getElementById('api-key-input').value; | ||||
|             if (apiKey) { | ||||
|                 localStorage.setItem('freezone_api_key', apiKey); | ||||
|                 alert('API key saved! It will be included in all requests.'); | ||||
|             } else { | ||||
|                 alert('Please enter an API key.'); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         function clearApiKey() { | ||||
|             localStorage.removeItem('freezone_api_key'); | ||||
|             document.getElementById('api-key-input').value = ''; | ||||
|             alert('API key cleared.'); | ||||
|         } | ||||
|     </script> | ||||
| </body> | ||||
| </html> | ||||
							
								
								
									
										153
									
								
								src/api/test-mock-api.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										153
									
								
								src/api/test-mock-api.sh
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,153 @@ | ||||
| #!/bin/bash | ||||
|  | ||||
| # Test script for the mock API server | ||||
| # Usage: ./test-mock-api.sh | ||||
|  | ||||
| BASE_URL="http://localhost:3000/api/mock" | ||||
| API_KEY="test-api-key" | ||||
|  | ||||
| echo "🧪 Testing Freezone Mock API Server" | ||||
| echo "🌐 Base URL: $BASE_URL" | ||||
| echo "🔑 API Key: $API_KEY" | ||||
| echo "----------------------------------------" | ||||
|  | ||||
| # Function to make API calls with proper formatting | ||||
| test_endpoint() { | ||||
|     local method=$1 | ||||
|     local endpoint=$2 | ||||
|     local description=$3 | ||||
|     local data=$4 | ||||
|      | ||||
|     echo "" | ||||
|     echo "📋 Testing: $description" | ||||
|     echo "🔗 $method $endpoint" | ||||
|      | ||||
|     if [ -n "$data" ]; then | ||||
|         echo "📤 Request Body:" | ||||
|         echo "$data" | jq '.' 2>/dev/null || echo "$data" | ||||
|         echo "" | ||||
|         response=$(curl -s -X "$method" \ | ||||
|             -H "Content-Type: application/json" \ | ||||
|             -H "X-API-Key: $API_KEY" \ | ||||
|             -d "$data" \ | ||||
|             "$BASE_URL$endpoint") | ||||
|     else | ||||
|         response=$(curl -s -X "$method" \ | ||||
|             -H "X-API-Key: $API_KEY" \ | ||||
|             "$BASE_URL$endpoint") | ||||
|     fi | ||||
|      | ||||
|     echo "📥 Response:" | ||||
|     echo "$response" | jq '.' 2>/dev/null || echo "$response" | ||||
|     echo "----------------------------------------" | ||||
| } | ||||
|  | ||||
| # Check if jq is available for JSON formatting | ||||
| if ! command -v jq &> /dev/null; then | ||||
|     echo "💡 Tip: Install 'jq' for better JSON formatting: brew install jq" | ||||
|     echo "" | ||||
| fi | ||||
|  | ||||
| # Check if mock server is running | ||||
| echo "🔍 Checking if mock server is running..." | ||||
| if ! curl -s "$BASE_URL" > /dev/null; then | ||||
|     echo "❌ Mock server is not running on $BASE_URL" | ||||
|     echo "💡 Start it with: ./start-mock-server.sh" | ||||
|     exit 1 | ||||
| fi | ||||
| echo "✅ Mock server is running!" | ||||
|  | ||||
| # Test Authentication | ||||
| test_endpoint "POST" "/auth/api-key" "Generate API Key" '{ | ||||
|   "client_id": "reseller_123", | ||||
|   "client_secret": "secret_abc123xyz", | ||||
|   "access_password": "freezone_access_2024" | ||||
| }' | ||||
|  | ||||
| # Test Digital Residents | ||||
| test_endpoint "GET" "/digital-residents" "List Digital Residents" | ||||
|  | ||||
| test_endpoint "POST" "/digital-residents" "Create Digital Resident" '{ | ||||
|   "reseller_user_id": "reseller_user_456", | ||||
|   "email": "john.doe@example.com", | ||||
|   "first_name": "John", | ||||
|   "last_name": "Doe", | ||||
|   "kyc_provider_id": "kyc_john_doe_789", | ||||
|   "phone": "+1234567890" | ||||
| }' | ||||
|  | ||||
| test_endpoint "GET" "/digital-residents/fz_resident_abc123" "Get Digital Resident" | ||||
|  | ||||
| # Test Free Zone Companies | ||||
| test_endpoint "GET" "/free-zone-companies" "List Free Zone Companies" | ||||
|  | ||||
| test_endpoint "POST" "/free-zone-companies" "Create Free Zone Company" '{ | ||||
|   "reseller_company_id": "reseller_comp_123", | ||||
|   "name": "Tech Innovations FZC", | ||||
|   "type": "company", | ||||
|   "description": "Technology consulting company", | ||||
|   "registration_number": "FZC-2024-001", | ||||
|   "tax_id": "TAX123456789", | ||||
|   "address": { | ||||
|     "street": "123 Business Bay", | ||||
|     "city": "Dubai", | ||||
|     "state": "Dubai", | ||||
|     "postal_code": "12345", | ||||
|     "country": "AE" | ||||
|   }, | ||||
|   "shareholder_resident_ids": ["fz_resident_abc123", "fz_resident_def456"], | ||||
|   "crypto_wallets": [ | ||||
|     { | ||||
|       "public_key": "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa", | ||||
|       "chain": "bitcoin", | ||||
|       "label": "Main Bitcoin Wallet" | ||||
|     } | ||||
|   ] | ||||
| }' | ||||
|  | ||||
| test_endpoint "GET" "/free-zone-companies/fz_company_xyz789" "Get Free Zone Company" | ||||
|  | ||||
| # Test Invoices | ||||
| test_endpoint "GET" "/free-zone-companies/fz_company_xyz789/invoices" "List Company Invoices" | ||||
|  | ||||
| test_endpoint "POST" "/free-zone-companies/fz_company_xyz789/invoices" "Create Invoice" '{ | ||||
|   "reseller_invoice_id": "reseller_inv_456", | ||||
|   "invoice_number": "INV-2024-001", | ||||
|   "amount": 1500.00, | ||||
|   "currency": "USD", | ||||
|   "tax_amount": 150.00, | ||||
|   "description": "Consulting services for Q1 2024", | ||||
|   "line_items": [ | ||||
|     { | ||||
|       "description": "Strategy consulting", | ||||
|       "quantity": 10, | ||||
|       "unit_price": 150.00, | ||||
|       "amount": 1500.00 | ||||
|     } | ||||
|   ], | ||||
|   "issue_date": "2024-01-15", | ||||
|   "due_date": "2024-02-15" | ||||
| }' | ||||
|  | ||||
| test_endpoint "GET" "/free-zone-companies/fz_company_xyz789/invoices/fz_invoice_inv123" "Get Invoice" | ||||
|  | ||||
| # Test Expenses | ||||
| test_endpoint "GET" "/free-zone-companies/fz_company_xyz789/expenses" "List Company Expenses" | ||||
|  | ||||
| test_endpoint "POST" "/free-zone-companies/fz_company_xyz789/expenses" "Create Expense" '{ | ||||
|   "reseller_expense_id": "reseller_exp_456", | ||||
|   "amount": 500.00, | ||||
|   "currency": "USD", | ||||
|   "category": "office_supplies", | ||||
|   "description": "Office equipment purchase", | ||||
|   "vendor": "Office Depot", | ||||
|   "receipt_url": "https://receipts.example.com/receipt123.pdf", | ||||
|   "date": "2024-01-15" | ||||
| }' | ||||
|  | ||||
| test_endpoint "GET" "/free-zone-companies/fz_company_xyz789/expenses/fz_expense_exp123" "Get Expense" | ||||
|  | ||||
| echo "" | ||||
| echo "✅ Mock API testing completed!" | ||||
| echo "💡 All endpoints should return example responses from the OpenAPI specification" | ||||
| echo "🔧 Mock server provides realistic data for development and testing" | ||||
		Reference in New Issue
	
	Block a user