mockapi/templates/admin/endpoint_form.html
2026-03-16 05:47:01 +00:00

187 lines
No EOL
11 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

{% extends "base.html" %}
{% block title %}{{ action }} Endpoint - Mock API Admin{% endblock %}
{% block content %}
<div class="content-header">
<h1><i class="bi bi-pencil-square"></i> {{ action }} Endpoint</h1>
<p class="lead">Configure a mock API endpoint.</p>
</div>
{% if error %}
<div class="alert alert-danger alert-dismissible fade show" role="alert">
{{ error }}
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
{% endif %}
<div class="row">
<div class="col-lg-8">
<div class="card">
<div class="card-body">
<form method="post" action="{{ form_action }}" id="endpoint-form">
{% if endpoint and endpoint.id %}
<input type="hidden" name="id" value="{{ endpoint.id }}">
{% endif %}
<div class="row">
<div class="col-md-6 mb-3">
<label for="route" class="form-label">Route <span class="text-danger">*</span></label>
<input type="text" class="form-control {% if errors and errors.route %}is-invalid{% endif %}" id="route" name="route" value="{{ endpoint.route if endpoint else '' }}" placeholder="/api/users/{id}" required>
<div class="invalid-feedback">
{{ errors.route if errors and errors.route else 'Route must start with / and contain no consecutive slashes or ..' }}
</div>
<div class="form-text">
The path for the endpoint, e.g., <code>/api/users</code> or <code>/api/users/{id}</code>.
</div>
</div>
<div class="col-md-6 mb-3">
<label for="method" class="form-label">HTTP Method <span class="text-danger">*</span></label>
<select class="form-select {% if errors and errors.method %}is-invalid{% endif %}" id="method" name="method" required>
<option value="">Select method</option>
{% for m in ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'HEAD', 'OPTIONS', 'TRACE'] %}
<option value="{{ m }}" {% if endpoint and endpoint.method == m %}selected{% endif %}>{{ m }}</option>
{% endfor %}
</select>
<div class="invalid-feedback">
{{ errors.method if errors and errors.method else 'Please select a valid HTTP method.' }}
</div>
</div>
</div>
<div class="mb-3">
<label for="response_body" class="form-label">Response Body <span class="text-danger">*</span></label>
<textarea class="form-control {% if errors and errors.response_body %}is-invalid{% endif %}" id="response_body" name="response_body" rows="8" required>{{ endpoint.response_body if endpoint else '{\n "message": "Hello, world!",\n "timestamp": {{ timestamp }}\n}' }}</textarea>
<div class="invalid-feedback">
{{ errors.response_body if errors and errors.response_body else 'Response body is required.' }}
</div>
<div class="form-text">
Jinja2 template. Available variables: <code>path_*</code>, <code>query_*</code>, <code>header_*</code>, <code>body_*</code>, <code>timestamp</code>, <code>datetime</code>, <code>request_id</code>, <code>method</code>, <code>url</code>, <code>client_host</code>, and any custom variables defined below.
</div>
</div>
<div class="row">
<div class="col-md-4 mb-3">
<label for="response_code" class="form-label">Response Code</label>
<input type="number" class="form-control {% if errors and errors.response_code %}is-invalid{% endif %}" id="response_code" name="response_code" value="{{ endpoint.response_code if endpoint else 200 }}" min="100" max="599">
<div class="invalid-feedback">
{{ errors.response_code if errors and errors.response_code else 'Response code must be between 100 and 599.' }}
</div>
</div>
<div class="col-md-4 mb-3">
<label for="content_type" class="form-label">Content-Type</label>
<input type="text" class="form-control {% if errors and errors.content_type %}is-invalid{% endif %}" id="content_type" name="content_type" value="{{ endpoint.content_type if endpoint else 'application/json' }}" placeholder="application/json">
<div class="invalid-feedback">
{{ errors.content_type if errors and errors.content_type else 'Content-Type header value.' }}
</div>
</div>
<div class="col-md-4 mb-3">
<label for="delay_ms" class="form-label">Delay (ms)</label>
<input type="number" class="form-control {% if errors and errors.delay_ms %}is-invalid{% endif %}" id="delay_ms" name="delay_ms" value="{{ endpoint.delay_ms if endpoint else 0 }}" min="0" max="30000">
<div class="invalid-feedback">
{{ errors.delay_ms if errors and errors.delay_ms else 'Artificial delay in milliseconds (030000).' }}
</div>
</div>
</div>
<div class="row">
<div class="col-md-6 mb-3">
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch" id="is_active" name="is_active" {% if endpoint and endpoint.is_active %}checked{% endif %}>
<label class="form-check-label" for="is_active">Endpoint is active</label>
</div>
<div class="form-text">
Inactive endpoints will not be registered as routes.
</div>
</div>
</div>
<div class="mb-3">
<label for="variables" class="form-label">Default Variables (JSON)</label>
<textarea class="form-control {% if errors and errors.variables %}is-invalid{% endif %}" id="variables" name="variables" rows="4">{{ endpoint.variables | tojson(indent=2) if endpoint and endpoint.variables else '{\n "app": "mockapi"\n}' }}</textarea>
<div class="invalid-feedback">
{{ errors.variables if errors and errors.variables else 'Must be valid JSON.' }}
</div>
<div class="form-text">
Default template variables as a JSON object. Will be merged with request context.
</div>
</div>
<div class="mb-3">
<label for="headers" class="form-label">Custom Response Headers (JSON)</label>
<textarea class="form-control {% if errors and errors.headers %}is-invalid{% endif %}" id="headers" name="headers" rows="4">{{ endpoint.headers | tojson(indent=2) if endpoint and endpoint.headers else '{}' }}</textarea>
<div class="invalid-feedback">
{{ errors.headers if errors and errors.headers else 'Must be valid JSON.' }}
</div>
<div class="form-text">
Additional headers to include in the response, e.g., <code>{"X-Custom-Header": "value"}</code>.
</div>
</div>
<div class="d-grid gap-2 d-md-flex justify-content-md-end">
<a href="/admin/endpoints" class="btn btn-outline-secondary me-md-2">Cancel</a>
<button type="submit" class="btn btn-primary">Save Endpoint</button>
</div>
</form>
</div>
</div>
</div>
<div class="col-lg-4">
<div class="card">
<div class="card-header">
<h5 class="mb-0"><i class="bi bi-info-circle"></i> Help</h5>
</div>
<div class="card-body">
<h6>Route Parameters</h6>
<p>Use <code>{param}</code> in the route to capture path parameters. Example: <code>/api/users/{id}</code> will make <code>id</code> available as <code>{{ '{{ id }}' }}</code> or <code>{{ '{{ path_id }}' }}</code>.</p>
<h6>Template Variables</h6>
<ul class="small">
<li><code>path_*</code> path parameters</li>
<li><code>query_*</code> query parameters</li>
<li><code>header_*</code> request headers</li>
<li><code>body_*</code> request body fields (if JSON)</li>
<li><code>timestamp</code> Unix timestamp</li>
<li><code>datetime</code> formatted date/time</li>
<li><code>request_id</code> unique request ID</li>
</ul>
<h6>Example Response Body</h6>
<pre class="bg-light p-2 rounded"><code>{
"id": {{ '{{ path_id }}' }},
"name": "User {{ '{{ path_id }}' }}",
"timestamp": {{ '{{ timestamp }}' }},
"query": {{ '{{ query_search }}' | default('null') }}
}</code></pre>
</div>
</div>
</div>
</div>
{% endblock %}
{% block extra_scripts %}
<script>
// Simple JSON validation for textareas
function validateJSON(textareaId) {
const textarea = document.getElementById(textareaId);
try {
JSON.parse(textarea.value);
textarea.classList.remove('is-invalid');
return true;
} catch (e) {
textarea.classList.add('is-invalid');
return false;
}
}
document.getElementById('variables')?.addEventListener('blur', () => validateJSON('variables'));
document.getElementById('headers')?.addEventListener('blur', () => validateJSON('headers'));
document.getElementById('endpoint-form')?.addEventListener('submit', function(e) {
let valid = true;
if (!validateJSON('variables')) valid = false;
if (!validateJSON('headers')) valid = false;
if (!valid) e.preventDefault();
});
</script>
{% endblock %}