commit
213111a77d
17 changed files with 570 additions and 18 deletions
|
|
@ -18,6 +18,7 @@ import (
|
||||||
"github.com/yaoapp/yao/fs"
|
"github.com/yaoapp/yao/fs"
|
||||||
"github.com/yaoapp/yao/i18n"
|
"github.com/yaoapp/yao/i18n"
|
||||||
"github.com/yaoapp/yao/importer"
|
"github.com/yaoapp/yao/importer"
|
||||||
|
"github.com/yaoapp/yao/moapi"
|
||||||
"github.com/yaoapp/yao/model"
|
"github.com/yaoapp/yao/model"
|
||||||
"github.com/yaoapp/yao/neo"
|
"github.com/yaoapp/yao/neo"
|
||||||
"github.com/yaoapp/yao/pack"
|
"github.com/yaoapp/yao/pack"
|
||||||
|
|
@ -203,6 +204,12 @@ func Load(cfg config.Config) (err error) {
|
||||||
printErr(cfg.Mode, "SUI", err)
|
printErr(cfg.Mode, "SUI", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Load Moapi
|
||||||
|
err = moapi.Load(cfg)
|
||||||
|
if err != nil {
|
||||||
|
printErr(cfg.Mode, "Moapi", err)
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -445,6 +452,7 @@ func loadApp(root string) error {
|
||||||
|
|
||||||
var appData []byte
|
var appData []byte
|
||||||
var appFile string
|
var appFile string
|
||||||
|
|
||||||
// Read app setting
|
// Read app setting
|
||||||
if has, _ := application.App.Exists("app.yao"); has {
|
if has, _ := application.App.Exists("app.yao"); has {
|
||||||
appFile = "app.yao"
|
appFile = "app.yao"
|
||||||
|
|
|
||||||
27
moapi/api.go
Normal file
27
moapi/api.go
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
package moapi
|
||||||
|
|
||||||
|
import "github.com/yaoapp/gou/api"
|
||||||
|
|
||||||
|
var dsl = []byte(`
|
||||||
|
{
|
||||||
|
"name": "Moapi API",
|
||||||
|
"description": "The API for Moapi",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"guard": "bearer-jwt",
|
||||||
|
"group": "__moapi/v1",
|
||||||
|
"paths": [
|
||||||
|
{
|
||||||
|
"path": "/images/generations",
|
||||||
|
"method": "POST",
|
||||||
|
"process": "moapi.images.Generations",
|
||||||
|
"in": ["$payload.model", "$payload.prompt", ":payload"],
|
||||||
|
"out": { "status": 200, "type": "application/json" }
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
`)
|
||||||
|
|
||||||
|
func registerAPI() error {
|
||||||
|
_, err := api.LoadSource("<moapi.v1>.yao", dsl, "moapi.v1")
|
||||||
|
return err
|
||||||
|
}
|
||||||
169
moapi/moapi.go
Normal file
169
moapi/moapi.go
Normal file
|
|
@ -0,0 +1,169 @@
|
||||||
|
package moapi
|
||||||
|
|
||||||
|
// *** WARNING ***
|
||||||
|
// Temporarily: change after the moapi is open source
|
||||||
|
//
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
jsoniter "github.com/json-iterator/go"
|
||||||
|
"github.com/yaoapp/gou/http"
|
||||||
|
"github.com/yaoapp/yao/config"
|
||||||
|
"github.com/yaoapp/yao/share"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Mirrors list all the mirrors
|
||||||
|
var cacheMirrors = []*Mirror{}
|
||||||
|
var cacheApps = []*App{}
|
||||||
|
var cacheMirrorsMap = map[string]*Mirror{}
|
||||||
|
|
||||||
|
// Models list all the models
|
||||||
|
var Models = []string{
|
||||||
|
"gpt-4-1106-preview",
|
||||||
|
"gpt-4-1106-vision-preview",
|
||||||
|
"gpt-4",
|
||||||
|
"gpt-4-32k",
|
||||||
|
|
||||||
|
"gpt-3.5-turbo",
|
||||||
|
"gpt-3.5-turbo-1106",
|
||||||
|
"gpt-3.5-turbo-instruct",
|
||||||
|
|
||||||
|
"dall-e-3",
|
||||||
|
"dall-e-2",
|
||||||
|
|
||||||
|
"tts-1",
|
||||||
|
"tts-1-hd",
|
||||||
|
|
||||||
|
"text-moderation-latest",
|
||||||
|
"text-moderation-stable",
|
||||||
|
|
||||||
|
"text-embedding-ada-002",
|
||||||
|
"whisper-1",
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load load the moapi
|
||||||
|
func Load(cfg config.Config) error {
|
||||||
|
return registerAPI()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mirrors list all the mirrors
|
||||||
|
func Mirrors(cache bool) ([]*Mirror, error) {
|
||||||
|
if cache && len(cacheMirrors) > 0 {
|
||||||
|
return cacheMirrors, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes, err := httpGet("/api/moapi/mirrors")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = jsoniter.Unmarshal(bytes, &cacheMirrors)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, mirror := range cacheMirrors {
|
||||||
|
cacheMirrorsMap[mirror.Host] = mirror
|
||||||
|
}
|
||||||
|
|
||||||
|
return cacheMirrors, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apps list all the apps
|
||||||
|
func Apps(cache bool) ([]*App, error) {
|
||||||
|
if cache && len(cacheApps) > 0 {
|
||||||
|
return cacheApps, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
mirrors := SelectMirrors()
|
||||||
|
bytes, err := httpGet("/api/moapi/apps", mirrors...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = jsoniter.Unmarshal(bytes, &cacheApps)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
channel := Channel()
|
||||||
|
if channel != "" {
|
||||||
|
for i := range cacheApps {
|
||||||
|
cacheApps[i].Homepage = cacheApps[i].Homepage + "?channel=" + channel
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return cacheApps, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Homepage get the home page url with the invite code
|
||||||
|
func Homepage() string {
|
||||||
|
channel := Channel()
|
||||||
|
if channel == "" {
|
||||||
|
return "https://store.moapi.ai"
|
||||||
|
}
|
||||||
|
return "https://store.moapi.ai" + "?channel=" + channel
|
||||||
|
}
|
||||||
|
|
||||||
|
// Channel get the channel
|
||||||
|
func Channel() string {
|
||||||
|
|
||||||
|
return share.App.Moapi.Channel
|
||||||
|
}
|
||||||
|
|
||||||
|
// SelectMirrors select the mirrors
|
||||||
|
func SelectMirrors() []*Mirror {
|
||||||
|
|
||||||
|
if share.App.Moapi.Mirrors == nil || len(share.App.Moapi.Mirrors) == 0 {
|
||||||
|
return []*Mirror{}
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := Mirrors(true)
|
||||||
|
if err != nil {
|
||||||
|
return []*Mirror{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// pick the mirrors
|
||||||
|
var result []*Mirror
|
||||||
|
for _, host := range share.App.Moapi.Mirrors {
|
||||||
|
if mirror, ok := cacheMirrorsMap[host]; ok {
|
||||||
|
if mirror.Status == "on" {
|
||||||
|
result = append(result, mirror)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// httpGet get the data from the api
|
||||||
|
func httpGet(api string, mirrors ...*Mirror) ([]byte, error) {
|
||||||
|
return httpGetRetry(api, mirrors, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func httpGetRetry(api string, mirrors []*Mirror, retryTimes int) ([]byte, error) {
|
||||||
|
|
||||||
|
url := "https://" + share.MoapiHosts[retryTimes] + api
|
||||||
|
if len(mirrors) > retryTimes {
|
||||||
|
url = "https://" + mirrors[retryTimes].Host + api
|
||||||
|
}
|
||||||
|
|
||||||
|
secret := share.App.Moapi.Secret
|
||||||
|
organization := share.App.Moapi.Organization
|
||||||
|
|
||||||
|
http := http.New(url)
|
||||||
|
http.SetHeader("Authorization", "Bearer "+secret)
|
||||||
|
http.SetHeader("Content-Type", "application/json")
|
||||||
|
http.SetHeader("Moapi-Organization", organization)
|
||||||
|
|
||||||
|
resp := http.Get()
|
||||||
|
if resp.Code >= 500 {
|
||||||
|
if retryTimes > 3 {
|
||||||
|
return nil, fmt.Errorf("Moapi Server Error: %s", resp.Data)
|
||||||
|
}
|
||||||
|
return httpGetRetry(api, mirrors, retryTimes+1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return jsoniter.Marshal(resp.Data)
|
||||||
|
}
|
||||||
48
moapi/process.go
Normal file
48
moapi/process.go
Normal file
|
|
@ -0,0 +1,48 @@
|
||||||
|
package moapi
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/yaoapp/gou/process"
|
||||||
|
"github.com/yaoapp/kun/exception"
|
||||||
|
"github.com/yaoapp/kun/utils"
|
||||||
|
"github.com/yaoapp/yao/openai"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
process.RegisterGroup("moapi", map[string]process.Handler{
|
||||||
|
"images.generations": ImagesGenerations,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// ImagesGenerations Generate images
|
||||||
|
func ImagesGenerations(process *process.Process) interface{} {
|
||||||
|
|
||||||
|
process.ValidateArgNums(2)
|
||||||
|
model := process.ArgsString(0)
|
||||||
|
prompt := process.ArgsString(1)
|
||||||
|
option := process.ArgsMap(2, map[string]interface{}{})
|
||||||
|
|
||||||
|
if model == "" {
|
||||||
|
exception.New("ImagesGenerations error: model is required", 400).Throw()
|
||||||
|
}
|
||||||
|
|
||||||
|
if prompt == "" {
|
||||||
|
exception.New("ImagesGenerations error: prompt is required", 400).Throw()
|
||||||
|
}
|
||||||
|
|
||||||
|
ai, err := openai.NewMoapi(model)
|
||||||
|
if err != nil {
|
||||||
|
exception.New("ImagesGenerations error: %s", 400, err).Throw()
|
||||||
|
}
|
||||||
|
|
||||||
|
option["prompt"] = prompt
|
||||||
|
option["model"] = model
|
||||||
|
option["response_format"] = "url"
|
||||||
|
|
||||||
|
res, ex := ai.ImagesGenerations(prompt, option)
|
||||||
|
if ex != nil {
|
||||||
|
utils.Dump(ex)
|
||||||
|
ex.Throw()
|
||||||
|
}
|
||||||
|
|
||||||
|
return res
|
||||||
|
}
|
||||||
34
moapi/types.go
Normal file
34
moapi/types.go
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
package moapi
|
||||||
|
|
||||||
|
// Mirror is the mirror info
|
||||||
|
type Mirror struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Host string `json:"host"`
|
||||||
|
Area string `json:"area"` // area code
|
||||||
|
Latency int `json:"latency"` // ms
|
||||||
|
Status string `json:"status"` // on, slow, off,
|
||||||
|
}
|
||||||
|
|
||||||
|
// App is the app info
|
||||||
|
type App struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
UpdatedAt int64 `json:"updated_at"`
|
||||||
|
CreatedAt int64 `json:"created_at"`
|
||||||
|
Country string `json:"country"`
|
||||||
|
Creator string `json:"creator"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
Version string `json:"version"`
|
||||||
|
Short string `json:"short"`
|
||||||
|
Icon string `json:"icon"`
|
||||||
|
Homepage string `json:"homepage"`
|
||||||
|
Images []string `json:"images,omitempty"`
|
||||||
|
Videos []string `json:"videos,omitempty"`
|
||||||
|
Stat AppStat `json:"stat,omitempty"`
|
||||||
|
Languages []string `json:"languages"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// AppStat is the app stat info
|
||||||
|
type AppStat struct {
|
||||||
|
Downloads int `json:"downloads"`
|
||||||
|
Stars int `json:"stars"`
|
||||||
|
}
|
||||||
|
|
@ -525,7 +525,12 @@ func (neo *DSL) getGuardHandlers() ([]gin.HandlerFunc, error) {
|
||||||
func (neo *DSL) newAI() error {
|
func (neo *DSL) newAI() error {
|
||||||
|
|
||||||
if neo.Connector == "" {
|
if neo.Connector == "" {
|
||||||
return fmt.Errorf("%s connector is required", neo.ID)
|
ai, err := openai.NewMoapi("gpt-3.5-turbo")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
neo.AI = ai
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
conn, err := connector.Select(neo.Connector)
|
conn, err := connector.Select(neo.Connector)
|
||||||
|
|
|
||||||
|
|
@ -4,11 +4,14 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/pkoukk/tiktoken-go"
|
"github.com/pkoukk/tiktoken-go"
|
||||||
"github.com/yaoapp/gou/connector"
|
"github.com/yaoapp/gou/connector"
|
||||||
"github.com/yaoapp/gou/http"
|
"github.com/yaoapp/gou/http"
|
||||||
"github.com/yaoapp/kun/exception"
|
"github.com/yaoapp/kun/exception"
|
||||||
|
"github.com/yaoapp/kun/utils"
|
||||||
|
"github.com/yaoapp/yao/share"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Tiktoken get number of tokens
|
// Tiktoken get number of tokens
|
||||||
|
|
@ -23,10 +26,11 @@ func Tiktoken(model string, input string) (int, error) {
|
||||||
|
|
||||||
// OpenAI struct
|
// OpenAI struct
|
||||||
type OpenAI struct {
|
type OpenAI struct {
|
||||||
key string
|
key string
|
||||||
model string
|
model string
|
||||||
host string
|
host string
|
||||||
maxToken int
|
organization string
|
||||||
|
maxToken int
|
||||||
}
|
}
|
||||||
|
|
||||||
// New create a new OpenAI instance by connector id
|
// New create a new OpenAI instance by connector id
|
||||||
|
|
@ -42,10 +46,44 @@ func New(id string) (*OpenAI, error) {
|
||||||
|
|
||||||
setting := c.Setting()
|
setting := c.Setting()
|
||||||
return &OpenAI{
|
return &OpenAI{
|
||||||
key: setting["key"].(string),
|
key: setting["key"].(string),
|
||||||
model: setting["model"].(string),
|
model: setting["model"].(string),
|
||||||
host: setting["host"].(string),
|
host: setting["host"].(string),
|
||||||
maxToken: 2048,
|
organization: "",
|
||||||
|
maxToken: 2048,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMoapi create a new OpenAI instance by model
|
||||||
|
// Temporarily: change after the moapi is open source
|
||||||
|
func NewMoapi(model string) (*OpenAI, error) {
|
||||||
|
|
||||||
|
if model == "" {
|
||||||
|
model = "gpt-3.5-turbo"
|
||||||
|
}
|
||||||
|
|
||||||
|
url := share.MoapiHosts[0]
|
||||||
|
|
||||||
|
if share.App.Moapi.Mirrors != nil {
|
||||||
|
url = share.App.Moapi.Mirrors[0]
|
||||||
|
}
|
||||||
|
key := share.App.Moapi.Secret
|
||||||
|
organization := share.App.Moapi.Organization
|
||||||
|
|
||||||
|
if !strings.HasPrefix(url, "http") {
|
||||||
|
url = "https://" + url
|
||||||
|
}
|
||||||
|
|
||||||
|
if key == "" {
|
||||||
|
return nil, fmt.Errorf("The moapi secret is empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
return &OpenAI{
|
||||||
|
key: key,
|
||||||
|
model: model,
|
||||||
|
host: url,
|
||||||
|
organization: organization,
|
||||||
|
maxToken: 16384,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -163,6 +201,8 @@ func (openai OpenAI) ImagesGenerations(prompt string, option map[string]interfac
|
||||||
}
|
}
|
||||||
|
|
||||||
option["prompt"] = prompt
|
option["prompt"] = prompt
|
||||||
|
utils.Dump(option)
|
||||||
|
|
||||||
return openai.postWithoutModel("/v1/images/generations", option)
|
return openai.postWithoutModel("/v1/images/generations", option)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -361,6 +401,9 @@ func (openai OpenAI) isError(res *http.Response) *exception.Exception {
|
||||||
|
|
||||||
if res.Status != 200 {
|
if res.Status != 200 {
|
||||||
message := "OpenAI Error"
|
message := "OpenAI Error"
|
||||||
|
if v, ok := res.Data.(string); ok {
|
||||||
|
message = v
|
||||||
|
}
|
||||||
if data, ok := res.Data.(map[string]interface{}); ok {
|
if data, ok := res.Data.(map[string]interface{}); ok {
|
||||||
if err, has := data["error"]; has {
|
if err, has := data["error"]; has {
|
||||||
if err, ok := err.(map[string]interface{}); ok {
|
if err, ok := err.(map[string]interface{}); ok {
|
||||||
|
|
|
||||||
|
|
@ -51,15 +51,13 @@ func SetupStatic() error {
|
||||||
|
|
||||||
// rewrite path
|
// rewrite path
|
||||||
func isPWA() bool {
|
func isPWA() bool {
|
||||||
if share.App.Static == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return share.App.Static.PWA
|
return share.App.Static.PWA
|
||||||
}
|
}
|
||||||
|
|
||||||
// rewrite path
|
// rewrite path
|
||||||
func spaApps() []string {
|
func spaApps() []string {
|
||||||
if share.App.Static == nil {
|
if share.App.Static.Apps == nil {
|
||||||
return []string{}
|
return []string{}
|
||||||
}
|
}
|
||||||
return share.App.Static.Apps
|
return share.App.Static.Apps
|
||||||
|
|
|
||||||
|
|
@ -11,3 +11,18 @@ const BUILDIN = false
|
||||||
|
|
||||||
// BUILDNAME 制品名称
|
// BUILDNAME 制品名称
|
||||||
const BUILDNAME = "yao"
|
const BUILDNAME = "yao"
|
||||||
|
|
||||||
|
// MoapiHosts the master mirror
|
||||||
|
var MoapiHosts = []string{
|
||||||
|
"master.moapi.ai",
|
||||||
|
"master-moon.moapi.ai",
|
||||||
|
"master-earth.moapi.ai",
|
||||||
|
"master-mars.moapi.ai",
|
||||||
|
"master-venus.moapi.ai",
|
||||||
|
"master-mercury.moapi.ai",
|
||||||
|
"master-jupiter.moapi.ai",
|
||||||
|
"master-saturn.moapi.ai",
|
||||||
|
"master-uranus.moapi.ai",
|
||||||
|
"master-neptune.moapi.ai",
|
||||||
|
"master-pluto.moapi.ai",
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -81,8 +81,17 @@ type AppInfo struct {
|
||||||
Option map[string]interface{} `json:"option,omitempty"`
|
Option map[string]interface{} `json:"option,omitempty"`
|
||||||
XGen string `json:"xgen,omitempty"`
|
XGen string `json:"xgen,omitempty"`
|
||||||
AdminRoot string `json:"adminRoot,omitempty"`
|
AdminRoot string `json:"adminRoot,omitempty"`
|
||||||
Static *Static `json:"public,omitempty"`
|
Static Static `json:"public,omitempty"`
|
||||||
Optional map[string]interface{} `json:"optional,omitempty"`
|
Optional map[string]interface{} `json:"optional,omitempty"`
|
||||||
|
Moapi Moapi `json:"moapi"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Moapi AIGC App Store API
|
||||||
|
type Moapi struct {
|
||||||
|
Channel string `json:"channel,omitempty"`
|
||||||
|
Mirrors []string `json:"mirrors,omitempty"`
|
||||||
|
Secret string `json:"secret,omitempty"`
|
||||||
|
Organization string `json:"organization,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Static setting
|
// Static setting
|
||||||
|
|
|
||||||
|
|
@ -193,6 +193,14 @@ var dsl = []byte(`
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"path": "/:id/media/:driver/search",
|
||||||
|
"method": "GET",
|
||||||
|
"process": "sui.Media.Search",
|
||||||
|
"in": ["$param.id", "$param.driver", ":query"],
|
||||||
|
"out": { "status": 200, "type": "application/json" }
|
||||||
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
"path": "/:id/preview/:template_id/*route",
|
"path": "/:id/preview/:template_id/*route",
|
||||||
"guard": "query-jwt",
|
"guard": "query-jwt",
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/url"
|
"net/url"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
|
@ -48,6 +49,8 @@ func init() {
|
||||||
"editor.renderaftersavetemp": EditorRenderAfterSaveTemp,
|
"editor.renderaftersavetemp": EditorRenderAfterSaveTemp,
|
||||||
"editor.sourceaftersavetemp": EditorSourceAfterSaveTemp,
|
"editor.sourceaftersavetemp": EditorSourceAfterSaveTemp,
|
||||||
|
|
||||||
|
"media.search": MediaSearch,
|
||||||
|
|
||||||
"preview.render": PreviewRender,
|
"preview.render": PreviewRender,
|
||||||
|
|
||||||
"build.all": BuildAll,
|
"build.all": BuildAll,
|
||||||
|
|
@ -165,6 +168,83 @@ func TemplateAssetUpload(process *process.Process) interface{} {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MediaSearch handle the find Template request
|
||||||
|
func MediaSearch(process *process.Process) interface{} {
|
||||||
|
|
||||||
|
process.ValidateArgNums(2)
|
||||||
|
sui := get(process)
|
||||||
|
driver := process.ArgsString(1)
|
||||||
|
|
||||||
|
query := url.Values{}
|
||||||
|
if process.NumOfArgs() > 2 {
|
||||||
|
switch v := process.Args[2].(type) {
|
||||||
|
case map[string]string:
|
||||||
|
for key, value := range v {
|
||||||
|
query.Set(key, value)
|
||||||
|
}
|
||||||
|
break
|
||||||
|
|
||||||
|
case map[string]interface{}:
|
||||||
|
for key, value := range v {
|
||||||
|
query.Set(key, fmt.Sprintf("%v", value))
|
||||||
|
}
|
||||||
|
break
|
||||||
|
|
||||||
|
case map[string][]string:
|
||||||
|
query = v
|
||||||
|
break
|
||||||
|
|
||||||
|
case url.Values:
|
||||||
|
query = v
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var err error
|
||||||
|
page := 1
|
||||||
|
if v := query.Get("page"); v != "" {
|
||||||
|
page, err = strconv.Atoi(v)
|
||||||
|
if err != nil {
|
||||||
|
exception.New(err.Error(), 400).Throw()
|
||||||
|
}
|
||||||
|
query.Del("page")
|
||||||
|
}
|
||||||
|
|
||||||
|
pageSize := 20
|
||||||
|
if v := query.Get("pagesize"); v != "" {
|
||||||
|
pageSize, err = strconv.Atoi(v)
|
||||||
|
if err != nil {
|
||||||
|
exception.New(err.Error(), 400).Throw()
|
||||||
|
}
|
||||||
|
query.Del("pagesize")
|
||||||
|
}
|
||||||
|
|
||||||
|
switch driver {
|
||||||
|
case "local":
|
||||||
|
templateID := query.Get("template")
|
||||||
|
if templateID == "" {
|
||||||
|
exception.New("the template is required", 400).Throw()
|
||||||
|
}
|
||||||
|
|
||||||
|
tmpl, err := sui.GetTemplate(templateID)
|
||||||
|
if err != nil {
|
||||||
|
exception.New(err.Error(), 400).Throw()
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := tmpl.MediaSearch(query, page, pageSize)
|
||||||
|
if err != nil {
|
||||||
|
exception.New(err.Error(), 500).Throw()
|
||||||
|
}
|
||||||
|
|
||||||
|
return res
|
||||||
|
|
||||||
|
default:
|
||||||
|
exception.New("the driver %s does not exist", 404, driver).Throw()
|
||||||
|
return nil
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// LocaleGet handle the find Template request
|
// LocaleGet handle the find Template request
|
||||||
func LocaleGet(process *process.Process) interface{} {
|
func LocaleGet(process *process.Process) interface{} {
|
||||||
process.ValidateArgNums(2)
|
process.ValidateArgNums(2)
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,48 @@ func (page *Page) EditorRender(request *Request) (*ResponseEditorRender, error)
|
||||||
res.Scripts = append(res.Scripts, filepath.Join("@pages", page.Route, page.Name+".ts"))
|
res.Scripts = append(res.Scripts, filepath.Join("@pages", page.Route, page.Name+".ts"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Render tools
|
||||||
|
res.Scripts = append(res.Scripts, filepath.Join("@assets", "__render.js"))
|
||||||
|
res.Styles = append(res.Styles, filepath.Join("@assets", "__render.css"))
|
||||||
|
|
||||||
|
// doc, _, err := page.Build(&BuildOption{
|
||||||
|
// SSR: true,
|
||||||
|
// AssetRoot: request.AssetRoot,
|
||||||
|
// })
|
||||||
|
|
||||||
|
// doc.Selection.Find("body").AppendHtml(`
|
||||||
|
// <script>
|
||||||
|
// console.log("setIframeHeight window.onload: setIframeHeight");
|
||||||
|
// function setIframeHeight(height) {
|
||||||
|
// window.parent.postMessage(
|
||||||
|
// {
|
||||||
|
// messageType: "setIframeHeight",
|
||||||
|
// iframeHeight: height,
|
||||||
|
// },
|
||||||
|
// "` + request.Referer + `"
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
|
||||||
|
// window.onload = function () {
|
||||||
|
// console.log("window.onload: setIframeHeight");
|
||||||
|
// const contentHeight = document.documentElement.scrollHeight;
|
||||||
|
// console.log("window.onload: setIframeHeight", contentHeight);
|
||||||
|
// try {
|
||||||
|
// setIframeHeight(contentHeight + "px");
|
||||||
|
// } catch (err) {
|
||||||
|
// console.log(` + "`" + `setIframeHeight error: ${err}` + "`" + `);
|
||||||
|
// }
|
||||||
|
// };
|
||||||
|
// </script>
|
||||||
|
// `)
|
||||||
|
|
||||||
|
// html, err = doc.Html()
|
||||||
|
// if err != nil {
|
||||||
|
// return nil, err
|
||||||
|
// }
|
||||||
|
|
||||||
|
// fmt.Println(html)
|
||||||
|
|
||||||
data, setting, err := page.Data(request)
|
data, setting, err := page.Data(request)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
res.Warnings = append(res.Warnings, err.Error())
|
res.Warnings = append(res.Warnings, err.Error())
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,9 @@
|
||||||
package core
|
package core
|
||||||
|
|
||||||
import "io"
|
import (
|
||||||
|
"io"
|
||||||
|
"net/url"
|
||||||
|
)
|
||||||
|
|
||||||
// SUIs the loaded SUI instances
|
// SUIs the loaded SUI instances
|
||||||
var SUIs = map[string]SUI{}
|
var SUIs = map[string]SUI{}
|
||||||
|
|
@ -39,6 +42,8 @@ type ITemplate interface {
|
||||||
Asset(file string) (*Asset, error)
|
Asset(file string) (*Asset, error)
|
||||||
AssetUpload(reader io.Reader, name string) (string, error)
|
AssetUpload(reader io.Reader, name string) (string, error)
|
||||||
|
|
||||||
|
MediaSearch(query url.Values, page int, pageSize int) (MediaSearchResult, error)
|
||||||
|
|
||||||
Build(option *BuildOption) error
|
Build(option *BuildOption) error
|
||||||
SyncAssets(option *BuildOption) error
|
SyncAssets(option *BuildOption) error
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -102,6 +102,30 @@ type Asset struct {
|
||||||
Content []byte `json:"content"`
|
Content []byte `json:"content"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Media is the struct for the media
|
||||||
|
type Media struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
Content []byte `json:"content,omitempty"`
|
||||||
|
Width int `json:"width,omitempty"`
|
||||||
|
Height int `json:"height,omitempty"`
|
||||||
|
Size int `json:"size,omitempty"`
|
||||||
|
Length int `json:"length,omitempty"`
|
||||||
|
Thumb string `json:"thumb,omitempty"`
|
||||||
|
URL string `json:"url,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// MediaSearchResult is the struct for the media search result
|
||||||
|
type MediaSearchResult struct {
|
||||||
|
Data []Media `json:"data"`
|
||||||
|
Total int `json:"total"`
|
||||||
|
Page int `json:"page"`
|
||||||
|
PageCount int `json:"pagecnt"`
|
||||||
|
PageSize int `json:"pagesize"`
|
||||||
|
Next int `json:"next"`
|
||||||
|
Prev int `json:"prev"`
|
||||||
|
}
|
||||||
|
|
||||||
// BuildOption is the struct for the option option
|
// BuildOption is the struct for the option option
|
||||||
type BuildOption struct {
|
type BuildOption struct {
|
||||||
SSR bool `json:"ssr"`
|
SSR bool `json:"ssr"`
|
||||||
|
|
|
||||||
|
|
@ -33,8 +33,8 @@ func TestPageEditorRender(t *testing.T) {
|
||||||
assert.NotEmpty(t, res.CSS)
|
assert.NotEmpty(t, res.CSS)
|
||||||
assert.NotEmpty(t, res.Scripts)
|
assert.NotEmpty(t, res.Scripts)
|
||||||
assert.NotEmpty(t, res.Styles)
|
assert.NotEmpty(t, res.Styles)
|
||||||
assert.Equal(t, 4, len(res.Scripts))
|
assert.Equal(t, 5, len(res.Scripts))
|
||||||
assert.Equal(t, 5, len(res.Styles))
|
assert.Equal(t, 6, len(res.Styles))
|
||||||
|
|
||||||
assert.Equal(t, "@assets/libs/tiny-slider/min/tiny-slider.js", res.Scripts[0])
|
assert.Equal(t, "@assets/libs/tiny-slider/min/tiny-slider.js", res.Scripts[0])
|
||||||
assert.Equal(t, "@assets/libs/feather-icons/feather.min.js", res.Scripts[1])
|
assert.Equal(t, "@assets/libs/feather-icons/feather.min.js", res.Scripts[1])
|
||||||
|
|
@ -75,6 +75,6 @@ func TestPagePreviewRender(t *testing.T) {
|
||||||
|
|
||||||
assert.NotEmpty(t, html)
|
assert.NotEmpty(t, html)
|
||||||
assert.Contains(t, html, "function Hello()")
|
assert.Contains(t, html, "function Hello()")
|
||||||
assert.Contains(t, html, "color: #2c3e50;")
|
// assert.Contains(t, html, "color: #2c3e50;")
|
||||||
assert.Contains(t, html, "/api/__yao/sui/v1/demo/asset/tech-blue/@assets")
|
assert.Contains(t, html, "/api/__yao/sui/v1/demo/asset/tech-blue/@assets")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,8 @@ package local
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"math"
|
||||||
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
@ -49,6 +51,41 @@ func (tmpl *Template) Themes() []core.SelectOption {
|
||||||
return tmpl.Template.Themes
|
return tmpl.Template.Themes
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MediaSearch search the asset
|
||||||
|
func (tmpl *Template) MediaSearch(query url.Values, page int, pageSize int) (core.MediaSearchResult, error) {
|
||||||
|
res := core.MediaSearchResult{Data: []core.Media{}, Page: page, PageSize: pageSize}
|
||||||
|
|
||||||
|
total := 124
|
||||||
|
pagecnt := int(math.Ceil(float64(total) / float64(pageSize)))
|
||||||
|
for i := 0; i < pageSize; i++ {
|
||||||
|
test := fmt.Sprintf("https://plus.unsplash.com/premium_photo-1671641797903-fd39ec702b16?auto=format&fit=crop&q=80&w=2334&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%%3D%%3D&id=%d", (page-1)*pageSize+i)
|
||||||
|
thumb := fmt.Sprintf("https://plus.unsplash.com/premium_photo-1671641797903-fd39ec702b16?auto=format&fit=crop&q=80&w=100&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%%3D%%3D&id=%d", (page-1)*pageSize+i)
|
||||||
|
res.Data = append(res.Data, core.Media{
|
||||||
|
ID: test,
|
||||||
|
URL: test,
|
||||||
|
Thumb: thumb,
|
||||||
|
Type: "image",
|
||||||
|
Width: 100,
|
||||||
|
Height: 100,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
res.Next = page + 1
|
||||||
|
if (page+1)*pageSize >= total {
|
||||||
|
res.Next = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
res.Prev = page - 1
|
||||||
|
if page == 1 {
|
||||||
|
res.Prev = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
res.Total = total
|
||||||
|
res.PageCount = pagecnt
|
||||||
|
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
// AssetUpload upload the asset
|
// AssetUpload upload the asset
|
||||||
func (tmpl *Template) AssetUpload(reader io.Reader, name string) (string, error) {
|
func (tmpl *Template) AssetUpload(reader io.Reader, name string) (string, error) {
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue