Move http endpoints into seperate route
This commit is contained in:
342
app.py
342
app.py
@@ -13,6 +13,7 @@ from dotenv import load_dotenv
|
|||||||
from routes.timer import timer
|
from routes.timer import timer
|
||||||
from routes.test import test
|
from routes.test import test
|
||||||
from routes.home import home
|
from routes.home import home
|
||||||
|
from routes.http import http
|
||||||
from flask_apscheduler import APScheduler
|
from flask_apscheduler import APScheduler
|
||||||
import asyncio
|
import asyncio
|
||||||
import aiohttp
|
import aiohttp
|
||||||
@@ -37,6 +38,7 @@ init_app(app)
|
|||||||
app.register_blueprint(timer, url_prefix='/timer')
|
app.register_blueprint(timer, url_prefix='/timer')
|
||||||
app.register_blueprint(test, url_prefix='/test')
|
app.register_blueprint(test, url_prefix='/test')
|
||||||
app.register_blueprint(home, url_prefix='/home')
|
app.register_blueprint(home, url_prefix='/home')
|
||||||
|
app.register_blueprint(http, url_prefix='/dashboard/http_functions')
|
||||||
|
|
||||||
class User(UserMixin):
|
class User(UserMixin):
|
||||||
def __init__(self, id, username, password_hash, created_at):
|
def __init__(self, id, username, password_hash, created_at):
|
||||||
@@ -126,140 +128,6 @@ def dashboard():
|
|||||||
http_functions = create_http_functions_view_model(http_functions)
|
http_functions = create_http_functions_view_model(http_functions)
|
||||||
return render_template("dashboard/http_functions/overview.html", http_functions=http_functions)
|
return render_template("dashboard/http_functions/overview.html", http_functions=http_functions)
|
||||||
|
|
||||||
@ app.route("/dashboard/http_functions", methods=["GET"])
|
|
||||||
@login_required
|
|
||||||
def dashboard_http_functions():
|
|
||||||
user_id = current_user.id
|
|
||||||
http_functions = db.get_http_functions_for_user(user_id)
|
|
||||||
http_functions = create_http_functions_view_model(http_functions)
|
|
||||||
if htmx:
|
|
||||||
return render_block(app.jinja_env, "dashboard/http_functions/overview.html", "page", http_functions=http_functions)
|
|
||||||
return render_template("dashboard/http_functions/overview.html", http_functions=http_functions)
|
|
||||||
|
|
||||||
|
|
||||||
@ app.route("/dashboard/http_functions/add_form", methods=["GET"])
|
|
||||||
@login_required
|
|
||||||
def get_http_function_add_form():
|
|
||||||
user_id = current_user.id
|
|
||||||
if htmx:
|
|
||||||
return render_block(app.jinja_env, 'dashboard/http_functions/new.html', 'page', user_id=user_id, name=DEFAULT_FUNCTION_NAME, script=DEFAULT_SCRIPT, environment_info=DEFAULT_ENVIRONMENT, is_public=False, log_request=True, log_response=False)
|
|
||||||
return render_template("dashboard/http_functions/new.html", user_id=user_id, name=DEFAULT_FUNCTION_NAME, script=DEFAULT_SCRIPT, environment_info=DEFAULT_ENVIRONMENT, is_public=False, log_request=True, log_response=False)
|
|
||||||
|
|
||||||
|
|
||||||
@ app.route("/dashboard/http_functions/create", methods=["POST"])
|
|
||||||
@login_required
|
|
||||||
def create_http_function():
|
|
||||||
try:
|
|
||||||
user_id = current_user.id
|
|
||||||
name = request.form.get('name')
|
|
||||||
script_content = request.form.get('script_content')
|
|
||||||
environment_info = request.form.get('environment_info')
|
|
||||||
is_public = request.form.get('is_public')
|
|
||||||
log_request = request.form.get('log_request')
|
|
||||||
log_response = request.form.get('log_response')
|
|
||||||
|
|
||||||
db.create_new_http_function(user_id, name, script_content, environment_info, is_public, log_request, log_response)
|
|
||||||
|
|
||||||
http_functions = db.get_http_functions_for_user(user_id)
|
|
||||||
http_functions = create_http_functions_view_model(http_functions)
|
|
||||||
return render_block(app.jinja_env, "dashboard/http_functions/overview.html", "page", http_functions=http_functions), 200, {"HX-Push-Url": url_for('dashboard_http_functions')}
|
|
||||||
except Exception as e:
|
|
||||||
print(e)
|
|
||||||
return { "status": "error", "message": str(e) }
|
|
||||||
|
|
||||||
|
|
||||||
@ app.route("/dashboard/http_functions/<int:function_id>/edit", methods=["POST"])
|
|
||||||
@login_required
|
|
||||||
def edit_http_function(function_id):
|
|
||||||
try:
|
|
||||||
user_id = current_user.id
|
|
||||||
name = request.json.get('name')
|
|
||||||
script_content = request.json.get('script_content')
|
|
||||||
environment_info = request.json.get('environment_info')
|
|
||||||
is_public = request.json.get('is_public')
|
|
||||||
log_request = request.json.get('log_request')
|
|
||||||
log_response = request.json.get('log_response')
|
|
||||||
|
|
||||||
updated_version = db.edit_http_function(user_id, function_id, name, script_content, environment_info, is_public, log_request, log_response)
|
|
||||||
|
|
||||||
return { "status": "success", "message": f'{name} updated' }
|
|
||||||
except Exception as e:
|
|
||||||
print(e)
|
|
||||||
return { "status": "error", "message": str(e) }
|
|
||||||
|
|
||||||
@ app.route("/dashboard/http_functions/<int:function_id>/delete", methods=["DELETE"])
|
|
||||||
@login_required
|
|
||||||
def delete_http_function(function_id):
|
|
||||||
try:
|
|
||||||
user_id = current_user.id
|
|
||||||
db.delete_http_function(user_id, function_id)
|
|
||||||
|
|
||||||
http_functions = db.get_http_functions_for_user(user_id)
|
|
||||||
http_functions = create_http_functions_view_model(http_functions)
|
|
||||||
return render_block(app.jinja_env, "dashboard/http_functions/overview.html", "page", http_functions=http_functions), 200, {"HX-Push-Url": url_for('dashboard_http_functions')}
|
|
||||||
except Exception as e:
|
|
||||||
return jsonify({"status": 'error', "message": str(e)}), 500
|
|
||||||
|
|
||||||
@ app.route("/dashboard/http_functions/<int:function_id>/logs", methods=["GET"])
|
|
||||||
@login_required
|
|
||||||
def get_http_function_logs(function_id):
|
|
||||||
user_id = current_user.id
|
|
||||||
http_function = db.get_http_function_by_id(user_id, function_id)
|
|
||||||
if not http_function:
|
|
||||||
return jsonify({'error': 'Function not found'}), 404
|
|
||||||
name = http_function['name']
|
|
||||||
http_function_invocations = db.get_http_function_invocations(function_id)
|
|
||||||
if htmx:
|
|
||||||
return render_block(app.jinja_env, 'dashboard/http_functions/logs.html', 'page', user_id=user_id, function_id=function_id, name=name, http_function_invocations=http_function_invocations)
|
|
||||||
return render_template("dashboard/http_functions/logs.html", user_id=user_id, name=name, function_id=function_id, http_function_invocations=http_function_invocations)
|
|
||||||
|
|
||||||
@ app.route("/dashboard/http_functions/<int:function_id>/client", methods=["GET"])
|
|
||||||
@login_required
|
|
||||||
def client(function_id):
|
|
||||||
user_id = current_user.id
|
|
||||||
http_function = db.get_http_function_by_id(user_id, function_id)
|
|
||||||
if not http_function:
|
|
||||||
return jsonify({'error': 'Function not found'}), 404
|
|
||||||
|
|
||||||
http_function = create_http_function_view_model(http_function)
|
|
||||||
if htmx:
|
|
||||||
return render_block(app.jinja_env, 'dashboard/http_functions/client.html', 'page', function_id=function_id, **http_function)
|
|
||||||
return render_template("dashboard/http_functions/client.html", function_id=function_id, **http_function)
|
|
||||||
|
|
||||||
@app.route("/dashboard/http_functions/<int:function_id>/history", methods=["GET"])
|
|
||||||
@login_required
|
|
||||||
def get_http_function_history(function_id):
|
|
||||||
user_id = current_user.id
|
|
||||||
http_function = db.get_http_function_by_id(user_id, function_id)
|
|
||||||
if not http_function:
|
|
||||||
return jsonify({'error': 'Function not found'}), 404
|
|
||||||
|
|
||||||
name = http_function['name']
|
|
||||||
version_number = http_function['version_number']
|
|
||||||
original_script = http_function['script_content'] if version_number == 1 else None
|
|
||||||
|
|
||||||
http_function_history = []
|
|
||||||
if version_number > 1:
|
|
||||||
raw_history = db.get_http_function_history(function_id)
|
|
||||||
|
|
||||||
for i in range(len(raw_history) - 1):
|
|
||||||
pre_version = raw_history[i + 1]
|
|
||||||
post_version = raw_history[i]
|
|
||||||
|
|
||||||
http_function_history.append({
|
|
||||||
'pre': pre_version['script_content'],
|
|
||||||
'post': post_version['script_content'],
|
|
||||||
'version_id': post_version['version_id'],
|
|
||||||
'version_number': post_version['version_number'],
|
|
||||||
'updated_at': post_version['updated_at']
|
|
||||||
})
|
|
||||||
|
|
||||||
if raw_history:
|
|
||||||
original_script = raw_history[-1]['script_content']
|
|
||||||
|
|
||||||
if htmx:
|
|
||||||
return render_block(app.jinja_env, 'dashboard/http_functions/history.html', 'page', user_id=user_id, function_id=function_id, name=name, http_function=http_function, http_function_history=http_function_history, original_script=original_script)
|
|
||||||
return render_template("dashboard/http_functions/history.html", user_id=user_id, name=name, function_id=function_id, http_function=http_function, http_function_history=http_function_history, original_script=original_script)
|
|
||||||
|
|
||||||
def _generate_script_from_natural_language(natural_query):
|
def _generate_script_from_natural_language(natural_query):
|
||||||
"""Generates a Javascript function from natural language using Gemini REST API."""
|
"""Generates a Javascript function from natural language using Gemini REST API."""
|
||||||
@@ -275,33 +143,68 @@ def _generate_script_from_natural_language(natural_query):
|
|||||||
prompt = f"""
|
prompt = f"""
|
||||||
You are an expert Javascript developer. Your task is to write a complete, production-ready Javascript async function based on the user's request.
|
You are an expert Javascript developer. Your task is to write a complete, production-ready Javascript async function based on the user's request.
|
||||||
|
|
||||||
|
**Function Signature:**
|
||||||
|
Your function MUST have the following signature: `async (req, environment) => {{ ... }}`
|
||||||
|
- `req`: An object containing details about the incoming HTTP request (e.g., `req.method`, `req.headers`, `req.body`, `req.query`).
|
||||||
|
- `environment`: A mutable JSON object that persists across executions. You can read and write to it to maintain state.
|
||||||
|
|
||||||
**Environment & Constraints:**
|
**Environment & Constraints:**
|
||||||
- The function will be executed in a simple, sandboxed Javascript environment.
|
- The function will be executed in a simple, sandboxed Javascript environment.
|
||||||
- **DO NOT** use `require()`, `import`, or any other module loading system. The environment does not support it.
|
- **CRITICAL**: ALL helper functions or variables MUST be defined *inside* the main `async` function. Do not define anything in the global scope.
|
||||||
|
- **DO NOT** use `require()`, `import`, or any other module loading system.
|
||||||
- **DO NOT** access the file system (`fs` module) or make network requests.
|
- **DO NOT** access the file system (`fs` module) or make network requests.
|
||||||
- The function must be a single `async` arrow function.
|
- You have access to a `console.log()` function for logging.
|
||||||
- You have access to a persistent JSON object called `environment`. You can read from and write to it. For example: `environment.my_variable = 'hello'`.
|
|
||||||
- You also have access to a `console.log()` function for logging.
|
|
||||||
|
|
||||||
**Input:**
|
**Response Helpers:**
|
||||||
The function receives one argument: `req`, an object containing details about the incoming HTTP request. It has properties like `req.method`, `req.headers`, `req.body`, `req.query`, etc.
|
You must use one of the following functions to return a response:
|
||||||
|
|
||||||
**Output:**
|
|
||||||
You must use one of the following helper functions to return a response:
|
|
||||||
- `HtmlResponse(body)`: Returns an HTML response.
|
- `HtmlResponse(body)`: Returns an HTML response.
|
||||||
- `JsonResponse(body)`: Returns a JSON response. The `body` will be automatically stringified.
|
- `JsonResponse(body)`: Returns a JSON response.
|
||||||
- `TextResponse(body)`: Returns a plain text response.
|
- `TextResponse(body)`: Returns a plain text response.
|
||||||
- `RedirectResponse(url)`: Redirects the user to a different URL.
|
- `RedirectResponse(url)`: Redirects the user.
|
||||||
|
|
||||||
**Example:**
|
**Complex Example (Tic-Tac-Toe):**
|
||||||
```javascript
|
```javascript
|
||||||
async (req) => {{
|
async (req, environment) => {{
|
||||||
if (!environment.counter) {{
|
// Helper function defined INSIDE the main function
|
||||||
environment.counter = 0;
|
function checkWinner(board) {{
|
||||||
|
const winConditions = [
|
||||||
|
[0, 1, 2], [3, 4, 5], [6, 7, 8],
|
||||||
|
[0, 3, 6], [1, 4, 7], [2, 5, 8],
|
||||||
|
[0, 4, 8], [2, 4, 6]
|
||||||
|
];
|
||||||
|
for (const condition of winConditions) {{
|
||||||
|
const [a, b, c] = condition;
|
||||||
|
if (board[a] && board[a] === board[b] && board[a] === board[c]) {{
|
||||||
|
return board[a];
|
||||||
|
}}
|
||||||
|
}}
|
||||||
|
return null;
|
||||||
}}
|
}}
|
||||||
environment.counter++;
|
|
||||||
console.log("The counter is now " + environment.counter);
|
if (!environment.board) {{
|
||||||
return HtmlResponse(`<h1>Counter: ${{environment.counter}}</h1>`);
|
environment.board = Array(9).fill("");
|
||||||
|
environment.turn = "X";
|
||||||
|
environment.winner = null;
|
||||||
|
}}
|
||||||
|
|
||||||
|
if (req.method === "POST" && req.json && req.json.move !== undefined) {{
|
||||||
|
const move = parseInt(req.json.move);
|
||||||
|
if (environment.board[move] === "" && !environment.winner) {{
|
||||||
|
environment.board[move] = environment.turn;
|
||||||
|
environment.turn = environment.turn === "X" ? "O" : "X";
|
||||||
|
environment.winner = checkWinner(environment.board);
|
||||||
|
}}
|
||||||
|
}}
|
||||||
|
|
||||||
|
const boardHTML = environment.board.map((cell, index) => `<button hx-post="/f/${{req.path.split('/')[2]}}/${{req.path.split('/')[3]}}" hx-target="body" hx-swap="innerHTML" name="move" value="${{index}}">${{cell || ' '}}</button>`).join("");
|
||||||
|
const message = environment.winner ? `Player ${{environment.winner}} wins!` : `Turn: ${{environment.turn}}`;
|
||||||
|
|
||||||
|
return HtmlResponse(`
|
||||||
|
<html>
|
||||||
|
<head><title>Tic-Tac-Toe</title><script src="https://unpkg.com/htmx.org@1.9.9"></script></head>
|
||||||
|
<body><h1>${{message}}</h1><div>${{boardHTML}}</div></body>
|
||||||
|
</html>
|
||||||
|
`);
|
||||||
}}
|
}}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -414,7 +317,18 @@ def execute_http_function(user_id, function):
|
|||||||
return jsonify({'error': 'Function not found'}), 404
|
return jsonify({'error': 'Function not found'}), 404
|
||||||
|
|
||||||
code = http_function['script_content']
|
code = http_function['script_content']
|
||||||
environment = http_function['environment_info']
|
environment_info = http_function['environment_info']
|
||||||
|
|
||||||
|
# Ensure environment is a dictionary
|
||||||
|
if isinstance(environment_info, str) and environment_info:
|
||||||
|
try:
|
||||||
|
environment = json.loads(environment_info)
|
||||||
|
except json.JSONDecodeError:
|
||||||
|
environment = {}
|
||||||
|
elif isinstance(environment_info, dict):
|
||||||
|
environment = environment_info
|
||||||
|
else:
|
||||||
|
environment = {}
|
||||||
is_public = http_function['is_public']
|
is_public = http_function['is_public']
|
||||||
log_request = http_function['log_request']
|
log_request = http_function['log_request']
|
||||||
log_response = http_function['log_response']
|
log_response = http_function['log_response']
|
||||||
@@ -538,129 +452,7 @@ def logout():
|
|||||||
logout_user()
|
logout_user()
|
||||||
return redirect(url_for('home'))
|
return redirect(url_for('home'))
|
||||||
|
|
||||||
@app.route("/http_function_editor/<int:function_id>", methods=["GET"])
|
|
||||||
@login_required
|
|
||||||
def http_function_editor(function_id):
|
|
||||||
user_id = current_user.id
|
|
||||||
http_function = db.get_http_function_by_id(user_id, function_id)
|
|
||||||
if not http_function:
|
|
||||||
return jsonify({'error': 'Function not found'}), 404
|
|
||||||
|
|
||||||
# Create a view model with all necessary data for the editor
|
|
||||||
editor_data = {
|
|
||||||
'id': http_function['id'],
|
|
||||||
'name': http_function['name'],
|
|
||||||
'script_content': http_function['script_content'],
|
|
||||||
'environment_info': json.dumps(http_function['environment_info'], indent=2),
|
|
||||||
'is_public': http_function['is_public'],
|
|
||||||
'log_request': http_function['log_request'],
|
|
||||||
'log_response': http_function['log_response'],
|
|
||||||
'version_number': http_function['version_number'],
|
|
||||||
'user_id': user_id,
|
|
||||||
'function_id': function_id,
|
|
||||||
# Add new URLs for navigation
|
|
||||||
'cancel_url': url_for('dashboard_http_functions'),
|
|
||||||
'edit_url': url_for('http_function_editor', function_id=function_id),
|
|
||||||
}
|
|
||||||
|
|
||||||
if htmx:
|
|
||||||
return render_block(app.jinja_env, "dashboard/http_functions/editor.html", "page", **editor_data)
|
|
||||||
|
|
||||||
return render_template("dashboard/http_functions/editor.html", **editor_data)
|
|
||||||
|
|
||||||
@app.route("/api/http_functions", methods=["POST"])
|
|
||||||
@login_required
|
|
||||||
def api_create_http_function():
|
|
||||||
try:
|
|
||||||
user_id = current_user.id
|
|
||||||
data = request.get_json()
|
|
||||||
name = data.get('name')
|
|
||||||
script_content = data.get('script_content')
|
|
||||||
environment_info = data.get('environment_info')
|
|
||||||
is_public = data.get('is_public')
|
|
||||||
log_request = data.get('log_request')
|
|
||||||
log_response = data.get('log_response')
|
|
||||||
|
|
||||||
# Check if function with same name already exists for this user
|
|
||||||
existing_function = db.get_http_function(user_id, name)
|
|
||||||
if existing_function:
|
|
||||||
return jsonify({
|
|
||||||
"status": "error",
|
|
||||||
"message": f"A function with the name '{name}' already exists"
|
|
||||||
}), 400
|
|
||||||
|
|
||||||
http_function = db.create_new_http_function(
|
|
||||||
user_id,
|
|
||||||
name,
|
|
||||||
script_content,
|
|
||||||
environment_info,
|
|
||||||
is_public,
|
|
||||||
log_request,
|
|
||||||
log_response
|
|
||||||
)
|
|
||||||
|
|
||||||
return jsonify({
|
|
||||||
"status": "success",
|
|
||||||
"message": f'{name} created',
|
|
||||||
"function": http_function
|
|
||||||
})
|
|
||||||
except Exception as e:
|
|
||||||
return jsonify({
|
|
||||||
"status": "error",
|
|
||||||
"message": str(e)
|
|
||||||
}), 400
|
|
||||||
|
|
||||||
@app.route("/api/http_functions/<int:function_id>", methods=["POST"])
|
|
||||||
@login_required
|
|
||||||
def api_update_http_function(function_id):
|
|
||||||
try:
|
|
||||||
user_id = current_user.id
|
|
||||||
data = request.get_json()
|
|
||||||
name = data.get('name')
|
|
||||||
script_content = data.get('script_content')
|
|
||||||
environment_info = data.get('environment_info')
|
|
||||||
is_public = data.get('is_public')
|
|
||||||
log_request = data.get('log_request')
|
|
||||||
log_response = data.get('log_response')
|
|
||||||
|
|
||||||
updated_function = db.edit_http_function(
|
|
||||||
user_id,
|
|
||||||
function_id,
|
|
||||||
name,
|
|
||||||
script_content,
|
|
||||||
environment_info,
|
|
||||||
is_public,
|
|
||||||
log_request,
|
|
||||||
log_response
|
|
||||||
)
|
|
||||||
|
|
||||||
return jsonify({
|
|
||||||
"status": "success",
|
|
||||||
"message": f'{name} updated',
|
|
||||||
"function": updated_function
|
|
||||||
})
|
|
||||||
except Exception as e:
|
|
||||||
return jsonify({
|
|
||||||
"status": "error",
|
|
||||||
"message": str(e)
|
|
||||||
}), 400
|
|
||||||
|
|
||||||
@app.route("/api/http_functions/<int:function_id>", methods=["DELETE"])
|
|
||||||
@login_required
|
|
||||||
def api_delete_http_function(function_id):
|
|
||||||
try:
|
|
||||||
user_id = current_user.id
|
|
||||||
db.delete_http_function(user_id, function_id)
|
|
||||||
|
|
||||||
return jsonify({
|
|
||||||
"status": "success",
|
|
||||||
"message": "Function deleted successfully"
|
|
||||||
})
|
|
||||||
except Exception as e:
|
|
||||||
return jsonify({
|
|
||||||
"status": "error",
|
|
||||||
"message": str(e)
|
|
||||||
}), 400
|
|
||||||
|
|
||||||
@login_manager.user_loader
|
@login_manager.user_loader
|
||||||
def load_user(user_id):
|
def load_user(user_id):
|
||||||
|
|||||||
360
routes/http.py
Normal file
360
routes/http.py
Normal file
@@ -0,0 +1,360 @@
|
|||||||
|
from flask import Blueprint, render_template, redirect, url_for, flash, request, jsonify
|
||||||
|
from jinja2_fragments import render_block
|
||||||
|
from werkzeug.security import generate_password_hash, check_password_hash
|
||||||
|
from flask_login import current_user, login_required
|
||||||
|
from extensions import db, htmx, environment
|
||||||
|
from datetime import datetime, timezone, timedelta
|
||||||
|
import json
|
||||||
|
from services import create_http_function_view_model, create_http_functions_view_model
|
||||||
|
|
||||||
|
'''
|
||||||
|
CREATE TABLE http_function_versions (
|
||||||
|
id SERIAL PRIMARY KEY,
|
||||||
|
http_function_id INT NOT NULL,
|
||||||
|
script_content TEXT NOT NULL,
|
||||||
|
version_number INT NOT NULL,
|
||||||
|
versioned_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
|
||||||
|
CONSTRAINT fk_http_function_versions
|
||||||
|
FOREIGN KEY (http_function_id)
|
||||||
|
REFERENCES http_functions (id)
|
||||||
|
ON DELETE CASCADE
|
||||||
|
ON UPDATE CASCADE
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE OR REPLACE FUNCTION fn_http_functions_versioning()
|
||||||
|
RETURNS TRIGGER
|
||||||
|
LANGUAGE plpgsql
|
||||||
|
AS $$
|
||||||
|
DECLARE
|
||||||
|
next_version INT;
|
||||||
|
BEGIN
|
||||||
|
IF TG_OP = 'INSERT' THEN
|
||||||
|
INSERT INTO http_function_versions (http_function_id, script_content, version_number)
|
||||||
|
VALUES (NEW.id, NEW.script_content, 1);
|
||||||
|
|
||||||
|
UPDATE http_functions
|
||||||
|
SET version_number = 1
|
||||||
|
WHERE id = NEW.id;
|
||||||
|
|
||||||
|
RETURN NEW;
|
||||||
|
|
||||||
|
ELSIF TG_OP = 'UPDATE' THEN
|
||||||
|
IF NEW.script_content IS DISTINCT FROM OLD.script_content THEN
|
||||||
|
SELECT COALESCE(MAX(version_number), 0) + 1
|
||||||
|
INTO next_version
|
||||||
|
FROM http_function_versions
|
||||||
|
WHERE http_function_id = NEW.id;
|
||||||
|
|
||||||
|
INSERT INTO http_function_versions (http_function_id, script_content, version_number)
|
||||||
|
VALUES (NEW.id, NEW.script_content, next_version);
|
||||||
|
|
||||||
|
UPDATE http_functions
|
||||||
|
SET version_number = next_version
|
||||||
|
WHERE id = NEW.id;
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
RETURN NEW;
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
RETURN NEW;
|
||||||
|
END;
|
||||||
|
$$;
|
||||||
|
|
||||||
|
CREATE TRIGGER tr_http_functions_versioning
|
||||||
|
AFTER INSERT OR UPDATE
|
||||||
|
ON http_functions
|
||||||
|
FOR EACH ROW
|
||||||
|
EXECUTE PROCEDURE fn_http_functions_versioning();
|
||||||
|
'''
|
||||||
|
|
||||||
|
DEFAULT_SCRIPT = """async (req) => {
|
||||||
|
console.log(`Method:${req.method}`)
|
||||||
|
console.log(`Generating ${environment.lines} random lines...`)
|
||||||
|
let svgContent = "";
|
||||||
|
|
||||||
|
for (let i = 0; i < environment.lines; i++) {
|
||||||
|
console.log(i)
|
||||||
|
let pathD = "M2 " + Math.random() * 79;
|
||||||
|
let circles = "";
|
||||||
|
|
||||||
|
for (let x = 12; x <= 202; x += 10) {
|
||||||
|
let y = Math.random() * 79
|
||||||
|
pathD += ` L${x} ${y}`;
|
||||||
|
circles += `<circle cx="${x}" cy="${y}" r="1"></circle>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
let pathColor = `rgb(${Math.random() * 255}, ${Math.random() * 255}, ${Math.random() * 255})`;
|
||||||
|
svgContent += `
|
||||||
|
<g style="fill: ${pathColor}; stroke: ${pathColor};">
|
||||||
|
<path d="${pathD}" fill="none" stroke="${pathColor}"></path>
|
||||||
|
${circles}
|
||||||
|
</g>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return HtmlResponse(`<svg viewBox="0 0 204 79" preserveAspectRatio="none">${svgContent}</svg>`);
|
||||||
|
}"""
|
||||||
|
|
||||||
|
DEFAULT_ENVIRONMENT = """{
|
||||||
|
"lines": 3
|
||||||
|
}"""
|
||||||
|
|
||||||
|
http = Blueprint('http', __name__)
|
||||||
|
|
||||||
|
@http.route("/", methods=["GET"])
|
||||||
|
@login_required
|
||||||
|
def dashboard_http_functions():
|
||||||
|
user_id = current_user.id
|
||||||
|
http_functions = db.get_http_functions_for_user(user_id)
|
||||||
|
http_functions = create_http_functions_view_model(http_functions)
|
||||||
|
if htmx:
|
||||||
|
return render_block(environment, "dashboard/http_functions/overview.html", "page", http_functions=http_functions)
|
||||||
|
return render_template("dashboard/http_functions/overview.html", http_functions=http_functions)
|
||||||
|
|
||||||
|
|
||||||
|
@http.route("/add_form", methods=["GET"])
|
||||||
|
@login_required
|
||||||
|
def get_http_function_add_form():
|
||||||
|
user_id = current_user.id
|
||||||
|
if htmx:
|
||||||
|
return render_block(environment, 'dashboard/http_functions/new.html', 'page', user_id=user_id, name='foo', script=DEFAULT_SCRIPT, environment_info=DEFAULT_ENVIRONMENT, is_public=False, log_request=True, log_response=False)
|
||||||
|
return render_template("dashboard/http_functions/new.html", user_id=user_id, name='foo', script=DEFAULT_SCRIPT, environment_info=DEFAULT_ENVIRONMENT, is_public=False, log_request=True, log_response=False)
|
||||||
|
|
||||||
|
|
||||||
|
@http.route("/create", methods=["POST"])
|
||||||
|
@login_required
|
||||||
|
def create_http_function():
|
||||||
|
try:
|
||||||
|
user_id = current_user.id
|
||||||
|
name = request.form.get('name')
|
||||||
|
script_content = request.form.get('script_content')
|
||||||
|
environment_info = request.form.get('environment_info')
|
||||||
|
is_public = request.form.get('is_public')
|
||||||
|
log_request = request.form.get('log_request')
|
||||||
|
log_response = request.form.get('log_response')
|
||||||
|
|
||||||
|
db.create_new_http_function(user_id, name, script_content, environment_info, is_public, log_request, log_response)
|
||||||
|
|
||||||
|
http_functions = db.get_http_functions_for_user(user_id)
|
||||||
|
http_functions = create_http_functions_view_model(http_functions)
|
||||||
|
return render_block(environment, "dashboard/http_functions/overview.html", "page", http_functions=http_functions), 200, {"HX-Push-Url": url_for('http.dashboard_http_functions')}
|
||||||
|
except Exception as e:
|
||||||
|
print(e)
|
||||||
|
return { "status": "error", "message": str(e) }
|
||||||
|
|
||||||
|
|
||||||
|
@http.route("/<int:function_id>/edit", methods=["POST"])
|
||||||
|
@login_required
|
||||||
|
def edit_http_function(function_id):
|
||||||
|
try:
|
||||||
|
user_id = current_user.id
|
||||||
|
name = request.json.get('name')
|
||||||
|
script_content = request.json.get('script_content')
|
||||||
|
environment_info = request.json.get('environment_info')
|
||||||
|
is_public = request.json.get('is_public')
|
||||||
|
log_request = request.json.get('log_request')
|
||||||
|
log_response = request.json.get('log_response')
|
||||||
|
|
||||||
|
updated_version = db.edit_http_function(user_id, function_id, name, script_content, environment_info, is_public, log_request, log_response)
|
||||||
|
|
||||||
|
return { "status": "success", "message": f'{name} updated' }
|
||||||
|
except Exception as e:
|
||||||
|
print(e)
|
||||||
|
return { "status": "error", "message": str(e) }
|
||||||
|
|
||||||
|
@http.route("/<int:function_id>/delete", methods=["DELETE"])
|
||||||
|
@login_required
|
||||||
|
def delete_http_function(function_id):
|
||||||
|
try:
|
||||||
|
user_id = current_user.id
|
||||||
|
db.delete_http_function(user_id, function_id)
|
||||||
|
|
||||||
|
http_functions = db.get_http_functions_for_user(user_id)
|
||||||
|
http_functions = create_http_functions_view_model(http_functions)
|
||||||
|
return render_block(environment, "dashboard/http_functions/overview.html", "page", http_functions=http_functions), 200, {"HX-Push-Url": url_for('http.dashboard_http_functions')}
|
||||||
|
except Exception as e:
|
||||||
|
return jsonify({"status": 'error', "message": str(e)}), 500
|
||||||
|
|
||||||
|
@http.route("/<int:function_id>/logs", methods=["GET"])
|
||||||
|
@login_required
|
||||||
|
def get_http_function_logs(function_id):
|
||||||
|
user_id = current_user.id
|
||||||
|
http_function = db.get_http_function_by_id(user_id, function_id)
|
||||||
|
if not http_function:
|
||||||
|
return jsonify({'error': 'Function not found'}), 404
|
||||||
|
name = http_function['name']
|
||||||
|
http_function_invocations = db.get_http_function_invocations(function_id)
|
||||||
|
if htmx:
|
||||||
|
return render_block(environment, 'dashboard/http_functions/logs.html', 'page', user_id=user_id, function_id=function_id, name=name, http_function_invocations=http_function_invocations)
|
||||||
|
return render_template("dashboard/http_functions/logs.html", user_id=user_id, name=name, function_id=function_id, http_function_invocations=http_function_invocations)
|
||||||
|
|
||||||
|
@http.route("/<int:function_id>/client", methods=["GET"])
|
||||||
|
@login_required
|
||||||
|
def client(function_id):
|
||||||
|
user_id = current_user.id
|
||||||
|
http_function = db.get_http_function_by_id(user_id, function_id)
|
||||||
|
if not http_function:
|
||||||
|
return jsonify({'error': 'Function not found'}), 404
|
||||||
|
|
||||||
|
http_function = create_http_function_view_model(http_function)
|
||||||
|
if htmx:
|
||||||
|
return render_block(environment, 'dashboard/http_functions/client.html', 'page', function_id=function_id, **http_function)
|
||||||
|
return render_template("dashboard/http_functions/client.html", function_id=function_id, **http_function)
|
||||||
|
|
||||||
|
@http.route("/<int:function_id>/history", methods=["GET"])
|
||||||
|
@login_required
|
||||||
|
def get_http_function_history(function_id):
|
||||||
|
user_id = current_user.id
|
||||||
|
http_function = db.get_http_function_by_id(user_id, function_id)
|
||||||
|
if not http_function:
|
||||||
|
return jsonify({'error': 'Function not found'}), 404
|
||||||
|
|
||||||
|
name = http_function['name']
|
||||||
|
version_number = http_function['version_number']
|
||||||
|
original_script = http_function['script_content'] if version_number == 1 else None
|
||||||
|
|
||||||
|
http_function_history = []
|
||||||
|
if version_number > 1:
|
||||||
|
raw_history = db.get_http_function_history(function_id)
|
||||||
|
|
||||||
|
for i in range(len(raw_history) - 1):
|
||||||
|
pre_version = raw_history[i + 1]
|
||||||
|
post_version = raw_history[i]
|
||||||
|
|
||||||
|
http_function_history.append({
|
||||||
|
'pre': pre_version['script_content'],
|
||||||
|
'post': post_version['script_content'],
|
||||||
|
'version_id': post_version['version_id'],
|
||||||
|
'version_number': post_version['version_number'],
|
||||||
|
'updated_at': post_version['updated_at']
|
||||||
|
})
|
||||||
|
|
||||||
|
if raw_history:
|
||||||
|
original_script = raw_history[-1]['script_content']
|
||||||
|
|
||||||
|
if htmx:
|
||||||
|
return render_block(environment, 'dashboard/http_functions/history.html', 'page', user_id=user_id, function_id=function_id, name=name, http_function=http_function, http_function_history=http_function_history, original_script=original_script)
|
||||||
|
return render_template("dashboard/http_functions/history.html", user_id=user_id, name=name, function_id=function_id, http_function=http_function, http_function_history=http_function_history, original_script=original_script)
|
||||||
|
|
||||||
|
@http.route("/editor/<int:function_id>", methods=["GET"])
|
||||||
|
@login_required
|
||||||
|
def http_function_editor(function_id):
|
||||||
|
user_id = current_user.id
|
||||||
|
http_function = db.get_http_function_by_id(user_id, function_id)
|
||||||
|
if not http_function:
|
||||||
|
return jsonify({'error': 'Function not found'}), 404
|
||||||
|
|
||||||
|
# Create a view model with all necessary data for the editor
|
||||||
|
editor_data = {
|
||||||
|
'id': http_function['id'],
|
||||||
|
'name': http_function['name'],
|
||||||
|
'script_content': http_function['script_content'],
|
||||||
|
'environment_info': json.dumps(http_function['environment_info'], indent=2),
|
||||||
|
'is_public': http_function['is_public'],
|
||||||
|
'log_request': http_function['log_request'],
|
||||||
|
'log_response': http_function['log_response'],
|
||||||
|
'version_number': http_function['version_number'],
|
||||||
|
'user_id': user_id,
|
||||||
|
'function_id': function_id,
|
||||||
|
# Add new URLs for navigation
|
||||||
|
'cancel_url': url_for('http.dashboard_http_functions'),
|
||||||
|
'edit_url': url_for('http.http_function_editor', function_id=function_id),
|
||||||
|
}
|
||||||
|
|
||||||
|
if htmx:
|
||||||
|
return render_block(environment, "dashboard/http_functions/editor.html", "page", **editor_data)
|
||||||
|
|
||||||
|
return render_template("dashboard/http_functions/editor.html", **editor_data)
|
||||||
|
@http.route("/api", methods=["POST"])
|
||||||
|
@login_required
|
||||||
|
def api_create_http_function():
|
||||||
|
try:
|
||||||
|
user_id = current_user.id
|
||||||
|
data = request.get_json()
|
||||||
|
name = data.get('name')
|
||||||
|
script_content = data.get('script_content')
|
||||||
|
environment_info = data.get('environment_info')
|
||||||
|
is_public = data.get('is_public')
|
||||||
|
log_request = data.get('log_request')
|
||||||
|
log_response = data.get('log_response')
|
||||||
|
|
||||||
|
# Check if function with same name already exists for this user
|
||||||
|
existing_function = db.get_http_function(user_id, name)
|
||||||
|
if existing_function:
|
||||||
|
return jsonify({
|
||||||
|
"status": "error",
|
||||||
|
"message": f"A function with the name '{name}' already exists"
|
||||||
|
}), 400
|
||||||
|
|
||||||
|
http_function = db.create_new_http_function(
|
||||||
|
user_id,
|
||||||
|
name,
|
||||||
|
script_content,
|
||||||
|
environment_info,
|
||||||
|
is_public,
|
||||||
|
log_request,
|
||||||
|
log_response
|
||||||
|
)
|
||||||
|
|
||||||
|
return jsonify({
|
||||||
|
"status": "success",
|
||||||
|
"message": f'{name} created',
|
||||||
|
"function": http_function
|
||||||
|
})
|
||||||
|
except Exception as e:
|
||||||
|
return jsonify({
|
||||||
|
"status": "error",
|
||||||
|
"message": str(e)
|
||||||
|
}), 400
|
||||||
|
|
||||||
|
@http.route("/api/<int:function_id>", methods=["POST"])
|
||||||
|
@login_required
|
||||||
|
def api_update_http_function(function_id):
|
||||||
|
try:
|
||||||
|
user_id = current_user.id
|
||||||
|
data = request.get_json()
|
||||||
|
name = data.get('name')
|
||||||
|
script_content = data.get('script_content')
|
||||||
|
environment_info = data.get('environment_info')
|
||||||
|
is_public = data.get('is_public')
|
||||||
|
log_request = data.get('log_request')
|
||||||
|
log_response = data.get('log_response')
|
||||||
|
|
||||||
|
updated_function = db.edit_http_function(
|
||||||
|
user_id,
|
||||||
|
function_id,
|
||||||
|
name,
|
||||||
|
script_content,
|
||||||
|
environment_info,
|
||||||
|
is_public,
|
||||||
|
log_request,
|
||||||
|
log_response
|
||||||
|
)
|
||||||
|
|
||||||
|
return jsonify({
|
||||||
|
"status": "success",
|
||||||
|
"message": f'{name} updated',
|
||||||
|
"function": updated_function
|
||||||
|
})
|
||||||
|
except Exception as e:
|
||||||
|
return jsonify({
|
||||||
|
"status": "error",
|
||||||
|
"message": str(e)
|
||||||
|
}), 400
|
||||||
|
|
||||||
|
@http.route("/api/<int:function_id>", methods=["DELETE"])
|
||||||
|
@login_required
|
||||||
|
def api_delete_http_function(function_id):
|
||||||
|
try:
|
||||||
|
user_id = current_user.id
|
||||||
|
db.delete_http_function(user_id, function_id)
|
||||||
|
|
||||||
|
return jsonify({
|
||||||
|
"status": "success",
|
||||||
|
"message": "Function deleted successfully"
|
||||||
|
})
|
||||||
|
except Exception as e:
|
||||||
|
return jsonify({
|
||||||
|
"status": "error",
|
||||||
|
"message": str(e)
|
||||||
|
}), 400
|
||||||
96
static/js/mithril/FunctionHistory.js
Normal file
96
static/js/mithril/FunctionHistory.js
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
const FunctionHistory = {
|
||||||
|
oninit: function(vnode) {
|
||||||
|
// Initialize indices in state
|
||||||
|
vnode.state.leftIndex = vnode.attrs.versions.length - 1; // Earliest
|
||||||
|
vnode.state.rightIndex = 0; // Latest
|
||||||
|
},
|
||||||
|
view: function (vnode) {
|
||||||
|
const versions = vnode.attrs.versions;
|
||||||
|
return m("div", [
|
||||||
|
m("div.flex.gap-4.mb-4", [
|
||||||
|
m("div.flex-1", [
|
||||||
|
m("label.block.text-sm.font-medium.text-gray-700", "Left Version"),
|
||||||
|
m("select.mt-1.block.w-full.rounded-md.border-gray-300.shadow-sm", {
|
||||||
|
onchange: (e) => {
|
||||||
|
vnode.state.leftIndex = parseInt(e.target.value);
|
||||||
|
vnode.state.leftVersion = versions[vnode.state.leftIndex];
|
||||||
|
vnode.state.updateDiff();
|
||||||
|
},
|
||||||
|
value: vnode.state.leftIndex
|
||||||
|
}, versions.map((v, idx) =>
|
||||||
|
m("option", { value: idx }, `Version ${v.version_number} (${new Date(v.versioned_at).toLocaleString()})`)
|
||||||
|
))
|
||||||
|
]),
|
||||||
|
m("div.flex-1", [
|
||||||
|
m("label.block.text-sm.font-medium.text-gray-700", "Right Version"),
|
||||||
|
m("select.mt-1.block.w-full.rounded-md.border-gray-300.shadow-sm", {
|
||||||
|
onchange: (e) => {
|
||||||
|
vnode.state.rightIndex = parseInt(e.target.value);
|
||||||
|
vnode.state.rightVersion = versions[vnode.state.rightIndex];
|
||||||
|
vnode.state.updateDiff();
|
||||||
|
},
|
||||||
|
value: vnode.state.rightIndex
|
||||||
|
}, versions.map((v, idx) =>
|
||||||
|
m("option", { value: idx }, `Version ${v.version_number} (${new Date(v.versioned_at).toLocaleString()})`)
|
||||||
|
))
|
||||||
|
])
|
||||||
|
]),
|
||||||
|
m("div", {
|
||||||
|
id: "diff-container",
|
||||||
|
style: "height: 500px; position: relative;"
|
||||||
|
})
|
||||||
|
]);
|
||||||
|
},
|
||||||
|
oncreate: function (vnode) {
|
||||||
|
const versions = vnode.attrs.versions;
|
||||||
|
// Initialize with the earliest and most recent versions if available
|
||||||
|
if (versions.length >= 2) {
|
||||||
|
vnode.state.leftVersion = versions[versions.length - 1]; // Earliest version
|
||||||
|
vnode.state.rightVersion = versions[0]; // Latest version
|
||||||
|
} else if (versions.length === 1) {
|
||||||
|
vnode.state.leftVersion = versions[0];
|
||||||
|
vnode.state.rightVersion = versions[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
vnode.state.updateDiff = function () {
|
||||||
|
if (vnode.state.aceDiffer) {
|
||||||
|
// Clean up previous instance
|
||||||
|
vnode.state.aceDiffer.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
vnode.state.aceDiffer = new AceDiff({
|
||||||
|
element: '#diff-container',
|
||||||
|
mode: "ace/mode/javascript",
|
||||||
|
left: {
|
||||||
|
content: vnode.state.leftVersion ? vnode.state.leftVersion.script : "",
|
||||||
|
editable: false,
|
||||||
|
},
|
||||||
|
right: {
|
||||||
|
content: vnode.state.rightVersion ? vnode.state.rightVersion.script : "",
|
||||||
|
editable: false,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Configure both editors
|
||||||
|
const editors = vnode.state.aceDiffer.getEditors();
|
||||||
|
['left', 'right'].forEach(side => {
|
||||||
|
editors[side].setOptions({
|
||||||
|
maxLines: 20,
|
||||||
|
autoScrollEditorIntoView: true,
|
||||||
|
});
|
||||||
|
editors[side].session.setOption("useWorker", false);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// Initial diff setup
|
||||||
|
vnode.state.updateDiff();
|
||||||
|
},
|
||||||
|
onremove: function (vnode) {
|
||||||
|
// Clean up AceDiff when component is removed
|
||||||
|
if (vnode.state.aceDiffer) {
|
||||||
|
vnode.state.aceDiffer.destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default FunctionHistory;
|
||||||
@@ -33,7 +33,7 @@
|
|||||||
Home
|
Home
|
||||||
</a><a
|
</a><a
|
||||||
class="flex items-center gap-3 rounded-lg px-3 py-2 text-gray-500 transition-all hover:text-gray-900 dark:text-gray-400 dark:hover:text-gray-50 cursor-pointer"
|
class="flex items-center gap-3 rounded-lg px-3 py-2 text-gray-500 transition-all hover:text-gray-900 dark:text-gray-400 dark:hover:text-gray-50 cursor-pointer"
|
||||||
data-id="15" hx-get="{{ url_for('dashboard_http_functions') }}" hx-target="#container"
|
data-id="15" hx-get="{{ url_for('http.dashboard_http_functions') }}" hx-target="#container"
|
||||||
hx-swap="innerHTML" hx-push-url="true"><svg xmlns="http://www.w3.org/2000/svg" width="18"
|
hx-swap="innerHTML" hx-push-url="true"><svg xmlns="http://www.w3.org/2000/svg" width="18"
|
||||||
height="18" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"
|
height="18" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"
|
||||||
class="size-6">
|
class="size-6">
|
||||||
|
|||||||
@@ -270,7 +270,7 @@
|
|||||||
class="inline-flex items-center justify-center px-4 py-2 bg-blue-600 hover:bg-blue-700 text-white font-medium rounded-lg transition-colors duration-200">
|
class="inline-flex items-center justify-center px-4 py-2 bg-blue-600 hover:bg-blue-700 text-white font-medium rounded-lg transition-colors duration-200">
|
||||||
View Timer Functions
|
View Timer Functions
|
||||||
</a>
|
</a>
|
||||||
<a href="{{ url_for('dashboard_http_functions') }}"
|
<a href="{{ url_for('http.dashboard_http_functions') }}"
|
||||||
class="inline-flex items-center justify-center px-4 py-2 bg-indigo-600 hover:bg-indigo-700 text-white font-medium rounded-lg transition-colors duration-200">
|
class="inline-flex items-center justify-center px-4 py-2 bg-indigo-600 hover:bg-indigo-700 text-white font-medium rounded-lg transition-colors duration-200">
|
||||||
View HTTP Functions
|
View HTTP Functions
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@@ -8,10 +8,10 @@ show_edit_form=True,
|
|||||||
show_logs=True,
|
show_logs=True,
|
||||||
show_client=True,
|
show_client=True,
|
||||||
show_history=True,
|
show_history=True,
|
||||||
edit_url=url_for('http_function_editor', function_id=function_id),
|
edit_url=url_for('http.http_function_editor', function_id=function_id),
|
||||||
cancel_url=url_for('dashboard_http_functions'),
|
cancel_url=url_for('http.dashboard_http_functions'),
|
||||||
logs_url=url_for('get_http_function_logs', function_id=function_id),
|
logs_url=url_for('http.get_http_function_logs', function_id=function_id),
|
||||||
history_url=url_for('get_http_function_history', function_id=function_id)) }}
|
history_url=url_for('http.get_http_function_history', function_id=function_id)) }}
|
||||||
|
|
||||||
<div class="mx-auto w-full pt-4" id="client-u{{ user_id }}-f{{ function_id }}">
|
<div class="mx-auto w-full pt-4" id="client-u{{ user_id }}-f{{ function_id }}">
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -10,8 +10,8 @@ show_client=True,
|
|||||||
show_history=True,
|
show_history=True,
|
||||||
edit_url=edit_url,
|
edit_url=edit_url,
|
||||||
cancel_url=cancel_url,
|
cancel_url=cancel_url,
|
||||||
logs_url=url_for('get_http_function_logs', function_id=function_id),
|
logs_url=url_for('http.get_http_function_logs', function_id=function_id),
|
||||||
history_url=url_for('get_http_function_history', function_id=function_id)) }}
|
history_url=url_for('http.get_http_function_history', function_id=function_id)) }}
|
||||||
|
|
||||||
|
|
||||||
<div id="app" class="p-1">
|
<div id="app" class="p-1">
|
||||||
@@ -33,8 +33,8 @@ history_url=url_for('get_http_function_history', function_id=function_id)) }}
|
|||||||
logResponse: {{ log_response | tojson }},
|
logResponse: {{ log_response | tojson }},
|
||||||
versionNumber: {{ version_number }},
|
versionNumber: {{ version_number }},
|
||||||
executeUrl: "{{ url_for('execute_code', playground='true') }}",
|
executeUrl: "{{ url_for('execute_code', playground='true') }}",
|
||||||
saveUrl: "{{ url_for('api_update_http_function', function_id=id) if id else url_for('api_create_http_function') }}",
|
saveUrl: "{{ url_for('http.api_update_http_function', function_id=id) if id else url_for('http.api_create_http_function') }}",
|
||||||
deleteUrl: "{{ url_for('api_delete_http_function', function_id=id) if id else '' }}",
|
deleteUrl: "{{ url_for('http.api_delete_http_function', function_id=id) if id else '' }}",
|
||||||
showDeleteButton: true
|
showDeleteButton: true
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -42,8 +42,8 @@
|
|||||||
{% if show_client|default(false, true) %}
|
{% if show_client|default(false, true) %}
|
||||||
<button
|
<button
|
||||||
class="group flex flex-col items-center {% if active_tab == 'client' %}text-blue-600{% else %}text-gray-500 hover:text-blue-600{% endif %}"
|
class="group flex flex-col items-center {% if active_tab == 'client' %}text-blue-600{% else %}text-gray-500 hover:text-blue-600{% endif %}"
|
||||||
hx-get="{{ url_for('client', function_id=function_id) }}" hx-target="#container" hx-swap="innerHTML"
|
hx-get="{{ url_for('http.client', function_id=function_id) }}" hx-target="#container"
|
||||||
hx-push-url="true">
|
hx-swap="innerHTML" hx-push-url="true">
|
||||||
<div
|
<div
|
||||||
class="p-2 rounded-lg {% if active_tab == 'client' %}bg-blue-50{% else %}group-hover:bg-blue-50{% endif %}">
|
class="p-2 rounded-lg {% if active_tab == 'client' %}bg-blue-50{% else %}group-hover:bg-blue-50{% endif %}">
|
||||||
<svg class="w-6 h-6" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
|
<svg class="w-6 h-6" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
|
||||||
|
|||||||
@@ -8,10 +8,10 @@ show_edit_form=True,
|
|||||||
show_logs=True,
|
show_logs=True,
|
||||||
show_client=True,
|
show_client=True,
|
||||||
show_history=True,
|
show_history=True,
|
||||||
edit_url=url_for('http_function_editor', function_id=function_id),
|
edit_url=url_for('http.http_function_editor', function_id=function_id),
|
||||||
cancel_url=url_for('dashboard_http_functions'),
|
cancel_url=url_for('http.dashboard_http_functions'),
|
||||||
logs_url=url_for('get_http_function_logs', function_id=function_id),
|
logs_url=url_for('http.get_http_function_logs', function_id=function_id),
|
||||||
history_url=url_for('get_http_function_history', function_id=function_id)) }}
|
history_url=url_for('http.get_http_function_history', function_id=function_id)) }}
|
||||||
|
|
||||||
<!-- Timeline -->
|
<!-- Timeline -->
|
||||||
<div>
|
<div>
|
||||||
|
|||||||
@@ -8,10 +8,10 @@ show_edit_form=True,
|
|||||||
show_logs=True,
|
show_logs=True,
|
||||||
show_client=True,
|
show_client=True,
|
||||||
show_history=True,
|
show_history=True,
|
||||||
edit_url=url_for('http_function_editor', function_id=function_id),
|
edit_url=url_for('http.http_function_editor', function_id=function_id),
|
||||||
cancel_url=url_for('dashboard_http_functions'),
|
cancel_url=url_for('http.dashboard_http_functions'),
|
||||||
logs_url=url_for('get_http_function_logs', function_id=function_id),
|
logs_url=url_for('http.get_http_function_logs', function_id=function_id),
|
||||||
history_url=url_for('get_http_function_history', function_id=function_id)) }}
|
history_url=url_for('http.get_http_function_history', function_id=function_id)) }}
|
||||||
|
|
||||||
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
|
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
|
||||||
<div class="bg-white rounded-lg shadow-sm border border-gray-200 overflow-hidden">
|
<div class="bg-white rounded-lg shadow-sm border border-gray-200 overflow-hidden">
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ show_refresh=False,
|
|||||||
show_logs=False,
|
show_logs=False,
|
||||||
show_client=False,
|
show_client=False,
|
||||||
show_link=False,
|
show_link=False,
|
||||||
dashboardUrl=url_for('dashboard_http_functions'),
|
dashboardUrl=url_for('http.dashboard_http_functions'),
|
||||||
cancel_url=url_for('dashboard_http_functions'),
|
cancel_url=url_for('http.dashboard_http_functions'),
|
||||||
title='New HTTP Function')
|
title='New HTTP Function')
|
||||||
}}
|
}}
|
||||||
|
|
||||||
@@ -32,9 +32,9 @@ title='New HTTP Function')
|
|||||||
logRequest: {{ log_request | tojson }},
|
logRequest: {{ log_request | tojson }},
|
||||||
logResponse: {{ log_response | tojson }},
|
logResponse: {{ log_response | tojson }},
|
||||||
executeUrl: "{{ url_for('execute_code', playground='true') }}",
|
executeUrl: "{{ url_for('execute_code', playground='true') }}",
|
||||||
saveUrl: "{{ url_for('api_create_http_function') }}",
|
saveUrl: "{{ url_for('http.api_create_http_function') }}",
|
||||||
showDeleteButton: false,
|
showDeleteButton: false,
|
||||||
dashboardUrl: "{{ url_for('dashboard_http_functions') }}"
|
dashboardUrl: "{{ url_for('http.dashboard_http_functions') }}"
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
<h1 class="text-2xl font-bold text-gray-900">HTTP Functions</h1>
|
<h1 class="text-2xl font-bold text-gray-900">HTTP Functions</h1>
|
||||||
<button
|
<button
|
||||||
class="inline-flex items-center px-4 py-2 ml-auto bg-green-600 hover:bg-green-700 text-white font-medium rounded-lg transition-colors duration-200"
|
class="inline-flex items-center px-4 py-2 ml-auto bg-green-600 hover:bg-green-700 text-white font-medium rounded-lg transition-colors duration-200"
|
||||||
hx-get="{{ url_for('get_http_function_add_form') }}" hx-target="#container" hx-swap="innerHTML"
|
hx-get="{{ url_for('http.get_http_function_add_form') }}" hx-target="#container" hx-swap="innerHTML"
|
||||||
hx-push-url="true">
|
hx-push-url="true">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5"
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5"
|
||||||
stroke="currentColor" class="w-5 h-5 mr-2">
|
stroke="currentColor" class="w-5 h-5 mr-2">
|
||||||
@@ -34,7 +34,7 @@
|
|||||||
<tr class="hover:bg-gray-50">
|
<tr class="hover:bg-gray-50">
|
||||||
<td class="px-6 py-4">
|
<td class="px-6 py-4">
|
||||||
<div class="flex items-center gap-2 cursor-pointer"
|
<div class="flex items-center gap-2 cursor-pointer"
|
||||||
hx-get="{{ url_for('http_function_editor', function_id=function.id) }}"
|
hx-get="{{ url_for('http.http_function_editor', function_id=function.id) }}"
|
||||||
hx-target="#container" hx-swap="innerHTML" hx-push-url="true">
|
hx-target="#container" hx-swap="innerHTML" hx-push-url="true">
|
||||||
<span class="font-medium text-gray-900">{{ function.name }}</span>
|
<span class="font-medium text-gray-900">{{ function.name }}</span>
|
||||||
<span
|
<span
|
||||||
@@ -76,7 +76,7 @@
|
|||||||
<div class="flex gap-3">
|
<div class="flex gap-3">
|
||||||
<button
|
<button
|
||||||
class="inline-flex items-center px-3 py-1.5 text-sm font-medium text-indigo-700 bg-indigo-50 rounded-md hover:bg-indigo-100 transition-colors duration-200"
|
class="inline-flex items-center px-3 py-1.5 text-sm font-medium text-indigo-700 bg-indigo-50 rounded-md hover:bg-indigo-100 transition-colors duration-200"
|
||||||
hx-get="{{ url_for('get_http_function_logs', function_id=function.id) }}"
|
hx-get="{{ url_for('http.get_http_function_logs', function_id=function.id) }}"
|
||||||
hx-target="#container" hx-swap="innerHTML" hx-push-url="true">
|
hx-target="#container" hx-swap="innerHTML" hx-push-url="true">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class="w-4 h-4 mr-1.5" fill="none"
|
<svg xmlns="http://www.w3.org/2000/svg" class="w-4 h-4 mr-1.5" fill="none"
|
||||||
viewBox="0 0 24 24" stroke="currentColor">
|
viewBox="0 0 24 24" stroke="currentColor">
|
||||||
@@ -87,8 +87,8 @@
|
|||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
class="inline-flex items-center px-3 py-1.5 text-sm font-medium text-indigo-700 bg-indigo-50 rounded-md hover:bg-indigo-100 transition-colors duration-200"
|
class="inline-flex items-center px-3 py-1.5 text-sm font-medium text-indigo-700 bg-indigo-50 rounded-md hover:bg-indigo-100 transition-colors duration-200"
|
||||||
hx-get="{{ url_for('client', function_id=function.id) }}" hx-target="#container"
|
hx-get="{{ url_for('http.client', function_id=function.id) }}"
|
||||||
hx-swap="innerHTML" hx-push-url="true">
|
hx-target="#container" hx-swap="innerHTML" hx-push-url="true">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class="w-4 h-4 mr-1.5" fill="none"
|
<svg xmlns="http://www.w3.org/2000/svg" class="w-4 h-4 mr-1.5" fill="none"
|
||||||
viewBox="0 0 24 24" stroke="currentColor">
|
viewBox="0 0 24 24" stroke="currentColor">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||||
|
|||||||
Reference in New Issue
Block a user