Skip to content

Commit

Permalink
[feat] SUI $backend function call supports custom Guard
Browse files Browse the repository at this point in the history
  • Loading branch information
trheyi committed Aug 5, 2024
1 parent 1ce87a1 commit 9977b62
Show file tree
Hide file tree
Showing 8 changed files with 447 additions and 332 deletions.
122 changes: 61 additions & 61 deletions data/bindata.go

Large diffs are not rendered by default.

435 changes: 220 additions & 215 deletions sui/api/api.go

Large diffs are not rendered by default.

27 changes: 25 additions & 2 deletions sui/api/guards.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"bytes"
"fmt"
"io"
"strconv"
"strings"

"github.com/gin-gonic/gin"
Expand Down Expand Up @@ -157,7 +158,7 @@ func (r *Request) scriptGuardExec(c *gin.Context, name string, args []interface{

script, err := v8.Select(id)
if err != nil {
c.JSON(403, gin.H{"code": 403, "message": fmt.Sprintf("Guard: %s %s", name, err.Error())})
c.JSON(403, gin.H{"code": 403, "message": err.Error()})
c.Abort()
return err
}
Expand Down Expand Up @@ -394,7 +395,29 @@ func (r *Request) scriptGuardExec(c *gin.Context, name string, args []interface{

_, err = ctx.Call(method, args...)
if err != nil {
c.JSON(403, gin.H{"code": 403, "message": fmt.Sprintf("Guard: %s %s", name, err.Error())})

message := err.Error()
if strings.HasPrefix(message, "Exception|") {
parts := strings.Split(message, ": ")

fmt.Println(parts)

if len(parts) > 1 {
codestr := strings.TrimPrefix(parts[0], "Exception|")
message := parts[1]
code := 403
if codestr != "" {
if v, err := strconv.Atoi(codestr); err == nil {
code = v
}
}
c.JSON(code, gin.H{"code": code, "message": message})
c.Abort()
return err
}
}

c.JSON(403, gin.H{"code": 403, "message": message})
c.Abort()
return err
}
Expand Down
141 changes: 106 additions & 35 deletions sui/api/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,24 @@ package api

import (
"path/filepath"
"strings"

"github.com/gin-gonic/gin"
jsoniter "github.com/json-iterator/go"
"github.com/yaoapp/gou/application"
"github.com/yaoapp/gou/process"
"github.com/yaoapp/kun/exception"
"github.com/yaoapp/kun/log"
"github.com/yaoapp/yao/sui/core"
)

var configs = map[string]*core.PageConfig{}
var chConfig = make(chan *core.PageConfig, 1)

func init() {
go configWriter()
}

// Run the backend script, with Api prefix method
func Run(process *process.Process) interface{} {
process.ValidateArgNums(3)
Expand Down Expand Up @@ -38,17 +48,6 @@ func Run(process *process.Process) interface{} {
return nil
}

if payload["page"] == nil {
exception.New("The page is required", 400).Throw()
return nil
}

page, ok := payload["page"].(string)
if !ok {
exception.New("The page must be a string", 400).Throw()
return nil
}

args := []interface{}{}
if payload["args"] != nil {
args, ok = payload["args"].([]interface{})
Expand All @@ -58,41 +57,39 @@ func Run(process *process.Process) interface{} {
}
}

ctx.Request.URL.Path = page
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)
// Load the script
file := filepath.Join("/public", route)

// Get the page config
cfg, err := getPageConfig(file, r.Request.DisableCache())
if err != nil {
log.Error("Can't load the page config (%s), %s", route, err.Error())
exception.New("Can't load the page config (%s), get more information from the log.", 500, route).Throw()
return nil
}

if c == nil {
c, _, err = r.MakeCache()
// Config and guard
prefix := "Api"
if cfg != nil {
_, err := r.apiGuard(method, cfg.API)
if err != nil {
log.Error("[SUI] Can't make cache, %s %s error: %s", route, method, err.Error())
exception.New("Can't make cache, the route and method is correct, get more information from the log.", 500).Throw()
log.Error("Guard error: %s", err.Error())
r.context.Done()
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
// Custom prefix
if cfg.API != nil && cfg.API.Prefix != "" {
prefix = cfg.API.Prefix
}
}

// Load the script
file := filepath.Join("/public", route)
script, err := core.LoadScript(file, r.Request.DisableCache())
if err != nil {
exception.New("Can't load the script (%s), get more information from the log.", 500, route).Throw()
Expand All @@ -106,22 +103,96 @@ func Run(process *process.Process) interface{} {

scriptCtx, err := 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) {
if !global.Has(prefix + method) {
exception.New("Method %s not found", 500, method).Throw()
return nil
}

res, err := scriptCtx.Call("Api"+method, args...)
res, err := scriptCtx.Call(prefix+method, args...)
if err != nil {
exception.Err(err, 500).Throw()
return nil
}

return res
}

// getPageConfig get the page config
func getPageConfig(file string, disableCache ...bool) (*core.PageConfig, error) {

// LOAD FROM CACHE
base := strings.TrimSuffix(strings.TrimSuffix(file, ".sui"), ".jit")
if disableCache == nil || !disableCache[0] {
if cfg, has := configs[base]; has {
return cfg, nil
}
}

file = base + ".cfg"
if exist, _ := application.App.Exists(file); !exist {
return nil, nil
}

source, err := application.App.Read(file)
if err != nil {
return nil, err
}

cfg := core.PageConfig{}
err = jsoniter.Unmarshal(source, &cfg)
if err != nil {
return nil, err
}

// Save to cache
go func() { chConfig <- &cfg }()
return &cfg, nil
}

func (r *Request) apiGuard(method string, api *core.PageAPI) (int, error) {
if api == nil {
return 200, nil
}

guard := api.DefaultGuard
if api.Guards != nil {
if g, has := api.Guards[method]; has {
guard = g
}
}

if guard == "" || guard == "-" {
return 200, nil
}

// Build in guard
if guard, has := Guards[guard]; has {
err := guard(r)
if err != nil {
return 403, err
}
return 200, nil
}

// Developer custom guard
err := r.processGuard(guard)
if err != nil {
return 403, err
}

return 200, nil
}

func configWriter() {
for {
select {
case config := <-chConfig:
configs[config.Root] = config
}
}
}
15 changes: 8 additions & 7 deletions sui/core/page.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,16 +61,17 @@ func (page *Page) GetConfig() *PageConfig {
// ExportConfig export the config
func (page *Page) ExportConfig() string {
if page.Config == nil {
return fmt.Sprintf(`{"cache_store": "%s"}`, page.CacheStore)
return fmt.Sprintf(`{"cacheStore": "%s"}`, page.CacheStore)
}

config, err := jsoniter.MarshalToString(map[string]interface{}{
"title": page.Config.Title,
"guard": page.Config.Guard,
"cache_store": page.CacheStore,
"cache": page.Config.Cache,
"data_cache": page.Config.DataCache,
"root": page.Root,
"title": page.Config.Title,
"guard": page.Config.Guard,
"cacheStore": page.CacheStore,
"cache": page.Config.Cache,
"dataCache": page.Config.DataCache,
"api": page.Config.API,
"root": page.Root,
})

if err != nil {
Expand Down
10 changes: 9 additions & 1 deletion sui/core/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -378,9 +378,10 @@ type PageSetting struct {
CacheStore string `json:"cache_store,omitempty"`
Cache int `json:"cache,omitempty"`
Root string `json:"root,omitempty"`
DataCache int `json:"data_cache,omitempty"`
DataCache int `json:"dataCache,omitempty"`
Description string `json:"description,omitempty"`
SEO *PageSEO `json:"seo,omitempty"`
API *PageAPI `json:"api,omitempty"`
}

// PageConfigRendered is the struct for the page config rendered
Expand All @@ -389,6 +390,13 @@ type PageConfigRendered struct {
Link string `json:"link,omitempty"`
}

// PageAPI is the struct for the page api
type PageAPI struct {
Prefix string `json:"prefix,omitempty"`
DefaultGuard string `json:"defaultGuard,omitempty"`
Guards map[string]string `json:"guards,omitempty"`
}

// PageSEO is the struct for the page seo
type PageSEO struct {
Title string `json:"title,omitempty"`
Expand Down
8 changes: 5 additions & 3 deletions sui/libsui/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -221,16 +221,18 @@ function __sui_store(elm) {

async function __sui_backend_call(
route: string,
page: string,
headers: [string, string][] | Record<string, string> | Headers,
method: string,
...args: any
): Promise<any> {
const url = `/api/__yao/sui/v1/run${route}`;
const headers = {
headers = {
"Content-Type": "application/json",
Referer: window.location.href,
Cookie: document.cookie,
...headers,
};
const payload = { method, args, page };
const payload = { method, args };
try {
const body = JSON.stringify(payload);
const response = await fetch(url, { method: "POST", headers, body: body });
Expand Down
21 changes: 13 additions & 8 deletions sui/libsui/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -176,25 +176,30 @@ class __Render {
}
}

function $Backend(route?: string) {
const page = window.location.pathname;
function $Backend(
route?: string,
headers?: [string, string][] | Record<string, string> | Headers
) {
const root = document.body.getAttribute("s:public") || "/";
route = route || page;
route = route || window.location.pathname;
const re = new RegExp("^" + root);
route = root + route.replace(re, "");
return new __Backend(route, page);
return new __Backend(route, headers);
}

class __Backend {
route = "";
page = "";
constructor(route: string, page: string) {
headers: [string, string][] | Record<string, string> | Headers = {};
constructor(
route: string,
headers: [string, string][] | Record<string, string> | Headers = {}
) {
this.route = route;
this.page = page;
this.headers = headers;
}

async Call(method: string, ...args: any): Promise<any> {
// @ts-ignore
return await __sui_backend_call(this.route, this.page, method, ...args);
return await __sui_backend_call(this.route, this.headers, method, ...args);
}
}

0 comments on commit 9977b62

Please sign in to comment.