This commit is contained in:
576
src/http_convert/web.py
Normal file
576
src/http_convert/web.py
Normal file
@@ -0,0 +1,576 @@
|
|||||||
|
from fastapi import FastAPI, Request, HTTPException
|
||||||
|
from fastapi.responses import HTMLResponse
|
||||||
|
from fastapi.staticfiles import StaticFiles
|
||||||
|
from pydantic import BaseModel
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Optional, Dict, Any, List
|
||||||
|
|
||||||
|
from .models import HTTPRequest, HttpMethod, OutputFormat
|
||||||
|
from .generators import Generator
|
||||||
|
from .parsers import Parser
|
||||||
|
from .highlighter import SyntaxHighlighter
|
||||||
|
|
||||||
|
|
||||||
|
app = FastAPI(title="HTTP Convert", description="HTTP Request Format Converter - Web Interface")
|
||||||
|
|
||||||
|
static_path = Path(__file__).parent.parent / "static"
|
||||||
|
if static_path.exists():
|
||||||
|
app.mount("/static", StaticFiles(directory=str(static_path)), name="static")
|
||||||
|
|
||||||
|
|
||||||
|
HTML_TEMPLATE = """
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>HTTP Convert - Request Builder</title>
|
||||||
|
<style>
|
||||||
|
* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||||
|
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
|
||||||
|
min-height: 100vh;
|
||||||
|
color: #fff;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
.container {
|
||||||
|
max-width: 1400px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
header {
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 30px;
|
||||||
|
}
|
||||||
|
h1 {
|
||||||
|
font-size: 2.5rem;
|
||||||
|
background: linear-gradient(90deg, #00d4ff, #7b2cbf);
|
||||||
|
-webkit-background-clip: text;
|
||||||
|
-webkit-text-fill-color: transparent;
|
||||||
|
background-clip: text;
|
||||||
|
}
|
||||||
|
.subtitle {
|
||||||
|
color: #888;
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
.main-content {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
gap: 30px;
|
||||||
|
}
|
||||||
|
@media (max-width: 1024px) {
|
||||||
|
.main-content {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.panel {
|
||||||
|
background: rgba(255, 255, 255, 0.05);
|
||||||
|
border-radius: 16px;
|
||||||
|
padding: 25px;
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||||
|
}
|
||||||
|
.panel-title {
|
||||||
|
font-size: 1.2rem;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
color: #00d4ff;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
.form-group {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
label {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #ccc;
|
||||||
|
}
|
||||||
|
input, select, textarea {
|
||||||
|
width: 100%;
|
||||||
|
padding: 12px 16px;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||||
|
border-radius: 8px;
|
||||||
|
background: rgba(255, 255, 255, 0.05);
|
||||||
|
color: #fff;
|
||||||
|
font-size: 14px;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
input:focus, select:focus, textarea:focus {
|
||||||
|
outline: none;
|
||||||
|
border-color: #00d4ff;
|
||||||
|
box-shadow: 0 0 0 3px rgba(0, 212, 255, 0.1);
|
||||||
|
}
|
||||||
|
textarea {
|
||||||
|
min-height: 120px;
|
||||||
|
font-family: 'Monaco', 'Consolas', monospace;
|
||||||
|
}
|
||||||
|
.row {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 2fr;
|
||||||
|
gap: 15px;
|
||||||
|
}
|
||||||
|
.btn {
|
||||||
|
padding: 14px 28px;
|
||||||
|
border: none;
|
||||||
|
border-radius: 8px;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
.btn-primary {
|
||||||
|
background: linear-gradient(90deg, #00d4ff, #7b2cbf);
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
.btn-primary:hover {
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 10px 30px rgba(0, 212, 255, 0.3);
|
||||||
|
}
|
||||||
|
.btn-secondary {
|
||||||
|
background: rgba(255, 255, 255, 0.1);
|
||||||
|
color: #fff;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||||
|
}
|
||||||
|
.btn-secondary:hover {
|
||||||
|
background: rgba(255, 255, 255, 0.2);
|
||||||
|
}
|
||||||
|
.output-tabs {
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
.tab {
|
||||||
|
padding: 10px 20px;
|
||||||
|
background: rgba(255, 255, 255, 0.1);
|
||||||
|
border: none;
|
||||||
|
border-radius: 8px;
|
||||||
|
color: #fff;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
.tab.active {
|
||||||
|
background: linear-gradient(90deg, #00d4ff, #7b2cbf);
|
||||||
|
}
|
||||||
|
.tab:hover:not(.active) {
|
||||||
|
background: rgba(255, 255, 255, 0.2);
|
||||||
|
}
|
||||||
|
.output-area {
|
||||||
|
background: #1e1e1e;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 20px;
|
||||||
|
min-height: 200px;
|
||||||
|
font-family: 'Monaco', 'Consolas', monospace;
|
||||||
|
font-size: 14px;
|
||||||
|
overflow-x: auto;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
.copy-btn {
|
||||||
|
position: absolute;
|
||||||
|
top: 10px;
|
||||||
|
right: 10px;
|
||||||
|
padding: 8px 16px;
|
||||||
|
background: rgba(255, 255, 255, 0.1);
|
||||||
|
border: none;
|
||||||
|
border-radius: 6px;
|
||||||
|
color: #fff;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
.copy-btn:hover {
|
||||||
|
background: rgba(255, 255, 255, 0.2);
|
||||||
|
}
|
||||||
|
.copy-btn.copied {
|
||||||
|
background: #4caf50;
|
||||||
|
}
|
||||||
|
.headers-section {
|
||||||
|
margin-top: 15px;
|
||||||
|
}
|
||||||
|
.header-row {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr 40px;
|
||||||
|
gap: 10px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.add-header-btn {
|
||||||
|
width: 100%;
|
||||||
|
padding: 10px;
|
||||||
|
background: rgba(255, 255, 255, 0.05);
|
||||||
|
border: 1px dashed rgba(255, 255, 255, 0.3);
|
||||||
|
border-radius: 8px;
|
||||||
|
color: #888;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
.add-header-btn:hover {
|
||||||
|
border-color: #00d4ff;
|
||||||
|
color: #00d4ff;
|
||||||
|
}
|
||||||
|
.remove-header {
|
||||||
|
background: #ff4757;
|
||||||
|
border: none;
|
||||||
|
border-radius: 6px;
|
||||||
|
color: #fff;
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 8px;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
.quick-actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
.quick-btn {
|
||||||
|
padding: 8px 16px;
|
||||||
|
background: rgba(255, 255, 255, 0.1);
|
||||||
|
border: none;
|
||||||
|
border-radius: 6px;
|
||||||
|
color: #fff;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 13px;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
.quick-btn:hover {
|
||||||
|
background: rgba(255, 255, 255, 0.2);
|
||||||
|
}
|
||||||
|
.input-section {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
.input-tabs {
|
||||||
|
display: flex;
|
||||||
|
gap: 5px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
.input-tab {
|
||||||
|
padding: 8px 16px;
|
||||||
|
background: rgba(255, 255, 255, 0.05);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||||
|
border-radius: 6px 6px 0 0;
|
||||||
|
color: #888;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
.input-tab.active {
|
||||||
|
background: rgba(255, 255, 255, 0.1);
|
||||||
|
color: #fff;
|
||||||
|
border-bottom-color: transparent;
|
||||||
|
}
|
||||||
|
.parser-area {
|
||||||
|
min-height: 150px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<header>
|
||||||
|
<h1>HTTP Convert</h1>
|
||||||
|
<p class="subtitle">Convert HTTP requests between cURL, HTTPie, fetch, and axios formats</p>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div class="main-content">
|
||||||
|
<div class="left-column">
|
||||||
|
<div class="panel">
|
||||||
|
<div class="panel-title">
|
||||||
|
<svg width="20" height="20" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"/>
|
||||||
|
</svg>
|
||||||
|
Build Request
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label>HTTP Method</label>
|
||||||
|
<select id="method">
|
||||||
|
<option value="GET">GET</option>
|
||||||
|
<option value="POST">POST</option>
|
||||||
|
<option value="PUT">PUT</option>
|
||||||
|
<option value="PATCH">PATCH</option>
|
||||||
|
<option value="DELETE">DELETE</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label>URL</label>
|
||||||
|
<input type="text" id="url" placeholder="https://api.example.com/users">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label>Headers</label>
|
||||||
|
<div id="headers-container"></div>
|
||||||
|
<button class="add-header-btn" onclick="addHeader()">+ Add Header</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label>Body (optional)</label>
|
||||||
|
<textarea id="body" placeholder="Enter request body (JSON, form data, etc.)"></textarea>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button class="btn btn-primary" onclick="generateOutput()">Generate Output</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="panel" style="margin-top: 20px;">
|
||||||
|
<div class="panel-title">
|
||||||
|
<svg width="20" height="20" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 9l3 3-3 3m5 0h3M5 20h14a2 2 0 002-2V6a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"/>
|
||||||
|
</svg>
|
||||||
|
Parse Input
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="input-tabs">
|
||||||
|
<button class="input-tab active" onclick="switchInputTab('curl')">cURL</button>
|
||||||
|
<button class="input-tab" onclick="switchInputTab('httpie')">HTTPie</button>
|
||||||
|
<button class="input-tab" onclick="switchInputTab('fetch')">fetch</button>
|
||||||
|
<button class="input-tab" onclick="switchInputTab('axios')">axios</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<textarea class="parser-area" id="parser-input" placeholder="Paste your cURL, HTTPie, fetch, or axios command here..."></textarea>
|
||||||
|
|
||||||
|
<button class="btn btn-secondary" style="margin-top: 15px;" onclick="parseInput()">Parse Input</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="right-column">
|
||||||
|
<div class="panel">
|
||||||
|
<div class="panel-title">
|
||||||
|
<svg width="20" height="20" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"/>
|
||||||
|
</svg>
|
||||||
|
Converted Output
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="output-tabs" id="output-tabs">
|
||||||
|
<button class="tab active" onclick="switchOutputTab('curl')">cURL</button>
|
||||||
|
<button class="tab" onclick="switchOutputTab('httpie')">HTTPie</button>
|
||||||
|
<button class="tab" onclick="switchOutputTab('fetch')">fetch</button>
|
||||||
|
<button class="tab" onclick="switchOutputTab('axios')">axios</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="output-area" id="output-content">
|
||||||
|
<button class="copy-btn" onclick="copyOutput(this)">Copy</button>
|
||||||
|
<pre id="output-text">Generated output will appear here...</pre>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style="margin-top: 15px; display: flex; gap: 10px; flex-wrap: wrap;">
|
||||||
|
<button class="quick-btn" onclick="loadExample('simple')">Simple GET</button>
|
||||||
|
<button class="quick-btn" onclick="loadExample('post')">POST with JSON</button>
|
||||||
|
<button class="quick-btn" onclick="loadExample('auth')">With Auth</button>
|
||||||
|
<button class="quick-btn" onclick="clearAll()">Clear All</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
let currentOutputFormat = 'curl';
|
||||||
|
let currentInputFormat = 'curl';
|
||||||
|
let parsedData = null;
|
||||||
|
|
||||||
|
function addHeader(key = '', value = '') {
|
||||||
|
const container = document.getElementById('headers-container');
|
||||||
|
const row = document.createElement('div');
|
||||||
|
row.className = 'header-row';
|
||||||
|
row.innerHTML = `
|
||||||
|
<input type="text" placeholder="Header name" value="${key}" class="header-key">
|
||||||
|
<input type="text" placeholder="Value" value="${value}" class="header-value">
|
||||||
|
<button class="remove-header" onclick="this.parentElement.remove()">×</button>
|
||||||
|
`;
|
||||||
|
container.appendChild(row);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getHeaders() {
|
||||||
|
const headers = {};
|
||||||
|
document.querySelectorAll('.header-row').forEach(row => {
|
||||||
|
const key = row.querySelector('.header-key').value.trim();
|
||||||
|
const value = row.querySelector('.header-value').value.trim();
|
||||||
|
if (key) headers[key] = value;
|
||||||
|
});
|
||||||
|
return headers;
|
||||||
|
}
|
||||||
|
|
||||||
|
function generateOutput() {
|
||||||
|
const method = document.getElementById('method').value;
|
||||||
|
const url = document.getElementById('url').value;
|
||||||
|
const headers = getHeaders();
|
||||||
|
const body = document.getElementById('body').value;
|
||||||
|
|
||||||
|
fetch('/api/convert', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {'Content-Type': 'application/json'},
|
||||||
|
body: JSON.stringify({
|
||||||
|
method,
|
||||||
|
url,
|
||||||
|
headers,
|
||||||
|
body: body || null,
|
||||||
|
format: currentOutputFormat
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.then(r => r.json())
|
||||||
|
.then(data => {
|
||||||
|
document.getElementById('output-text').textContent = data.result;
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
document.getElementById('output-text').textContent = 'Error: ' + err.message;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseInput() {
|
||||||
|
const input = document.getElementById('parser-input').value;
|
||||||
|
|
||||||
|
fetch('/api/parse', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {'Content-Type': 'application/json'},
|
||||||
|
body: JSON.stringify({
|
||||||
|
input,
|
||||||
|
format: currentInputFormat
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.then(r => r.json())
|
||||||
|
.then(data => {
|
||||||
|
if (data.error) {
|
||||||
|
alert('Parse error: ' + data.error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
parsedData = data;
|
||||||
|
document.getElementById('method').value = data.method;
|
||||||
|
document.getElementById('url').value = data.url;
|
||||||
|
document.getElementById('body').value = data.body || '';
|
||||||
|
document.getElementById('headers-container').innerHTML = '';
|
||||||
|
Object.entries(data.headers || {}).forEach(([k, v]) => addHeader(k, v));
|
||||||
|
generateOutput();
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
alert('Error: ' + err.message);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function switchOutputTab(format) {
|
||||||
|
currentOutputFormat = format;
|
||||||
|
document.querySelectorAll('#output-tabs .tab').forEach(t => t.classList.remove('active'));
|
||||||
|
event.target.classList.add('active');
|
||||||
|
if (parsedData || document.getElementById('url').value) {
|
||||||
|
generateOutput();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function switchInputTab(format) {
|
||||||
|
currentInputFormat = format;
|
||||||
|
document.querySelectorAll('.input-tab').forEach(t => t.classList.remove('active'));
|
||||||
|
event.target.classList.add('active');
|
||||||
|
}
|
||||||
|
|
||||||
|
function copyOutput(btn) {
|
||||||
|
navigator.clipboard.writeText(document.getElementById('output-text').textContent)
|
||||||
|
.then(() => {
|
||||||
|
btn.textContent = 'Copied!';
|
||||||
|
btn.classList.add('copied');
|
||||||
|
setTimeout(() => {
|
||||||
|
btn.textContent = 'Copy';
|
||||||
|
btn.classList.remove('copied');
|
||||||
|
}, 2000);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadExample(type) {
|
||||||
|
if (type === 'simple') {
|
||||||
|
document.getElementById('method').value = 'GET';
|
||||||
|
document.getElementById('url').value = 'https://api.example.com/users';
|
||||||
|
document.getElementById('body').value = '';
|
||||||
|
document.getElementById('headers-container').innerHTML = '';
|
||||||
|
} else if (type === 'post') {
|
||||||
|
document.getElementById('method').value = 'POST';
|
||||||
|
document.getElementById('url').value = 'https://api.example.com/users';
|
||||||
|
document.getElementById('body').value = JSON.stringify({name: 'John', email: 'john@example.com'}, null, 2);
|
||||||
|
document.getElementById('headers-container').innerHTML = '';
|
||||||
|
addHeader('Content-Type', 'application/json');
|
||||||
|
} else if (type === 'auth') {
|
||||||
|
document.getElementById('method').value = 'GET';
|
||||||
|
document.getElementById('url').value = 'https://api.example.com/protected';
|
||||||
|
document.getElementById('body').value = '';
|
||||||
|
document.getElementById('headers-container').innerHTML = '';
|
||||||
|
addHeader('Authorization', 'Bearer YOUR_TOKEN');
|
||||||
|
addHeader('Accept', 'application/json');
|
||||||
|
}
|
||||||
|
generateOutput();
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearAll() {
|
||||||
|
document.getElementById('method').value = 'GET';
|
||||||
|
document.getElementById('url').value = '';
|
||||||
|
document.getElementById('body').value = '';
|
||||||
|
document.getElementById('headers-container').innerHTML = '';
|
||||||
|
document.getElementById('parser-input').value = '';
|
||||||
|
document.getElementById('output-text').textContent = 'Generated output will appear here...';
|
||||||
|
parsedData = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
|
addHeader();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/", response_class=HTMLResponse)
|
||||||
|
async def root():
|
||||||
|
return HTML_TEMPLATE
|
||||||
|
|
||||||
|
|
||||||
|
class ConvertRequest(BaseModel):
|
||||||
|
method: str
|
||||||
|
url: str
|
||||||
|
headers: Dict[str, str] = {}
|
||||||
|
body: Optional[str] = None
|
||||||
|
format: str = "curl"
|
||||||
|
|
||||||
|
|
||||||
|
class ParseRequest(BaseModel):
|
||||||
|
input: str
|
||||||
|
format: str
|
||||||
|
|
||||||
|
|
||||||
|
@app.post("/api/convert")
|
||||||
|
async def convert(request: ConvertRequest):
|
||||||
|
try:
|
||||||
|
http_request = HTTPRequest(
|
||||||
|
method=HttpMethod(request.method),
|
||||||
|
url=request.url,
|
||||||
|
headers=request.headers,
|
||||||
|
body=request.body
|
||||||
|
)
|
||||||
|
output = Generator.generate(http_request, OutputFormat(request.format))
|
||||||
|
return {"result": output}
|
||||||
|
except Exception as e:
|
||||||
|
raise HTTPException(status_code=400, detail=str(e))
|
||||||
|
|
||||||
|
|
||||||
|
@app.post("/api/parse")
|
||||||
|
async def parse(request: ParseRequest):
|
||||||
|
try:
|
||||||
|
http_request = Parser.parse(request.input, request.format)
|
||||||
|
return {
|
||||||
|
"method": http_request.method.value,
|
||||||
|
"url": http_request.url,
|
||||||
|
"headers": http_request.headers,
|
||||||
|
"params": http_request.params,
|
||||||
|
"body": http_request.body,
|
||||||
|
"body_json": http_request.body_json
|
||||||
|
}
|
||||||
|
except Exception as e:
|
||||||
|
raise HTTPException(status_code=400, detail=str(e))
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/api/formats")
|
||||||
|
async def get_formats():
|
||||||
|
return {
|
||||||
|
"formats": [f.value for f in OutputFormat]
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user