Merge pull request #727 from trheyi/main

[feat] Add $Backend function call to directly invoke backend functions from frontend scripts.
This commit is contained in:
Max 2024-08-03 20:43:55 +08:00 committed by GitHub
commit 346a310eb6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 228 additions and 70 deletions

File diff suppressed because one or more lines are too long

View file

@ -31,7 +31,7 @@ func Render(process *process.Process) interface{} {
ctx.Request.URL.Path = route
r, _, err := NewRequestContext(ctx)
if err != nil {
return fmt.Sprintf("<div class='text-danger'> %s </div>", err.Error())
return fmt.Sprintf("<span class='sui-render-error'> %s </span>", err.Error())
}
var c *core.Cache = nil
@ -42,27 +42,33 @@ func Render(process *process.Process) interface{} {
if c == nil {
c, _, err = r.MakeCache()
if err != nil {
return fmt.Sprintf("<div class='text-danger'> %s </div>", err.Error())
return fmt.Sprintf("<span class='sui-render-error'> %s </span>", err.Error())
}
}
if c == nil {
return fmt.Sprintf("<div class='text-danger'> Cache not found </div>")
return fmt.Sprintf("<span class='sui-render-error'> Cache not found </span>")
}
// Guard the page
code, err := r.Guard(c)
if err != nil {
return fmt.Sprintf("<span class='sui-render-error'> %v %s </span>", code, err.Error())
}
data, ok := payload["data"].(map[string]interface{})
if !ok {
return fmt.Sprintf("<div class='text-danger'> Data not found </div>")
return fmt.Sprintf("<span class='sui-render-error'> Data not found </span>")
}
name, ok := payload["name"].(string)
if !ok {
return fmt.Sprintf("<div class='text-danger'> Name not found </div>")
return fmt.Sprintf("<span class='sui-render-error'> Name not found </span>")
}
html, err := r.renderHTML(c, name, c.HTML, core.Data(data))
if err != nil {
return fmt.Sprintf("<div class='text-danger'> %s </div>", err.Error())
return fmt.Sprintf("<span class='sui-render-error'> %s </span>", err.Error())
}
return html

View file

@ -1,8 +1,106 @@
package api
import "github.com/yaoapp/gou/process"
import (
"github.com/gin-gonic/gin"
"github.com/yaoapp/gou/process"
"github.com/yaoapp/kun/exception"
"github.com/yaoapp/kun/log"
"github.com/yaoapp/yao/sui/core"
)
// Run the backend script, with Api prefix method
func Run(process *process.Process) interface{} {
return nil
process.ValidateArgNums(3)
ctx, ok := process.Args[0].(*gin.Context)
if !ok {
exception.New("The context is required", 400).Throw()
return nil
}
ctx.Header("Content-Type", "text/html; charset=utf-8")
route := process.ArgsString(1)
payload := process.ArgsMap(2)
if route == "" {
exception.New("The route is required", 400).Throw()
return nil
}
if payload["method"] == nil {
exception.New("The method is required", 400).Throw()
return nil
}
method, ok := payload["method"].(string)
if !ok {
exception.New("The method must be a string", 400).Throw()
return nil
}
args := []interface{}{}
if payload["args"] != nil {
args, ok = payload["args"].([]interface{})
if !ok {
exception.New("The args must be an array", 400).Throw()
return nil
}
}
ctx.Request.URL.Path = route
r, _, err := NewRequestContext(ctx)
if err != nil {
exception.Err(err, 500).Throw()
return nil
}
var c *core.Cache = nil
if !r.Request.DisableCache() {
c = core.GetCache(r.File)
}
if c == nil {
c, _, err = r.MakeCache()
if err != nil {
log.Error("[SUI] Can't make cache, %s %s error: %s", route, method, err.Error())
exception.New("Can't make cache, please the route and method is correct, get more information from the log.", 500).Throw()
return nil
}
}
// Guard the page
code, err := r.Guard(c)
if err != nil {
exception.Err(err, code).Throw()
return nil
}
if c == nil {
exception.New("Cache not found", 500).Throw()
return nil
}
if c.Script == nil {
exception.New("Script not found", 500).Throw()
return nil
}
scriptCtx, err := c.Script.NewContext(process.Sid, nil)
if err != nil {
exception.Err(err, 500).Throw()
return nil
}
defer scriptCtx.Close()
global := scriptCtx.Global()
if !global.Has("Api" + method) {
exception.New("Method %s not found", 500, method).Throw()
return nil
}
res, err := scriptCtx.Call("Api"+method, args...)
if err != nil {
exception.Err(err, 500).Throw()
return nil
}
return res
}

View file

@ -219,6 +219,43 @@ function __sui_store(elm) {
};
}
async function __sui_backend_call(
route: string,
method: string,
...args: any
): Promise<any> {
const url = `/api/__yao/sui/v1/run${route}`;
const headers = {
"Content-Type": "application/json",
Cookie: document.cookie,
};
const payload = { method, args };
try {
const body = JSON.stringify(payload);
const response = await fetch(url, { method: "POST", headers, body: body });
const text = await response.text();
let data: any | null = null;
if (text && text != "") {
data = JSON.parse(text);
}
if (response.status >= 400) {
const message = data.message
? data.message
: `Failed to call ${route} ${method}`;
const code = data.code ? data.code : 500;
return Promise.reject({ message, code });
}
return Promise.resolve(data);
} catch (e) {
const message = e.message ? e.message : `Failed to call ${route} ${method}`;
const code = e.code ? e.code : 500;
console.error(`[SUI] Failed to call ${route} ${method}:`, e);
return Promise.reject({ message, code });
}
}
/**
* SUI Render
* @param component

View file

@ -170,8 +170,25 @@ class __Render {
this.comp = comp;
this.option = option;
}
async Render(name, data): Promise<string> {
async Exec(name, data): Promise<string> {
// @ts-ignore
return __sui_render(this.comp, name, data, this.option);
}
}
function $Backend(route?: string) {
route = route || window.location.pathname;
return new __Backend(route);
}
class __Backend {
route = "";
constructor(route) {
this.route = route;
}
async Call(method: string, ...args: any): Promise<any> {
// @ts-ignore
return await __sui_backend_call(this.route, method, ...args);
}
}