github.com/angenalZZZ/gofunc@v0.0.0-20210507121333-48ff1be3917b/js/goja.go (about) 1 package js 2 3 import ( 4 "bytes" 5 "encoding/xml" 6 "fmt" 7 "net/http" 8 "net/url" 9 "os" 10 "path/filepath" 11 "strings" 12 "time" 13 14 "github.com/angenalZZZ/gofunc/data/cache/fastcache" 15 "github.com/angenalZZZ/gofunc/data/id" 16 "github.com/angenalZZZ/gofunc/data/random" 17 "github.com/angenalZZZ/gofunc/f" 18 ht "github.com/angenalZZZ/gofunc/http" 19 "github.com/angenalZZZ/gofunc/log" 20 "github.com/dop251/goja" 21 "github.com/go-redis/redis/v7" 22 "github.com/go-resty/resty/v2" 23 "github.com/jmoiron/sqlx" 24 json "github.com/json-iterator/go" 25 "github.com/klauspost/crc32" 26 "github.com/nats-io/nats.go" 27 ) 28 29 // Logger use log in javascript. 30 // log.debug('%d', 123) 31 // log.info('%v', {name:'hello'}) 32 // log.warn() 33 // log.error() 34 // log.fatal() 35 // log.panic() 36 // log.log() 37 func Logger(r *goja.Runtime, log *log.Logger) { 38 logObj := r.NewObject() 39 40 // log.debug output log 41 _ = logObj.Set("debug", func(c goja.FunctionCall) goja.Value { 42 v, l := goja.Undefined(), len(c.Arguments) 43 if l == 0 { 44 return v 45 } 46 format, s := c.Arguments[0].String(), make([]interface{}, l-1) 47 for i := 1; i < l; i++ { 48 s = append(s, c.Arguments[i].Export()) 49 } 50 log.Debug().Msgf(format, s...) 51 return v 52 }) 53 // log.info output log 54 _ = logObj.Set("info", func(c goja.FunctionCall) goja.Value { 55 v, l := goja.Undefined(), len(c.Arguments) 56 if l == 0 { 57 return v 58 } 59 format, s := c.Arguments[0].String(), make([]interface{}, l-1) 60 for i := 1; i < l; i++ { 61 s = append(s, c.Arguments[i].Export()) 62 } 63 log.Info().Msgf(format, s...) 64 return v 65 }) 66 // log.warn output log 67 _ = logObj.Set("warn", func(c goja.FunctionCall) goja.Value { 68 v, l := goja.Undefined(), len(c.Arguments) 69 if l == 0 { 70 return v 71 } 72 format, s := c.Arguments[0].String(), make([]interface{}, l-1) 73 for i := 1; i < l; i++ { 74 s = append(s, c.Arguments[i].Export()) 75 } 76 log.Warn().Msgf(format, s...) 77 return v 78 }) 79 // log.error output log 80 _ = logObj.Set("error", func(c goja.FunctionCall) goja.Value { 81 v, l := goja.Undefined(), len(c.Arguments) 82 if l == 0 { 83 return v 84 } 85 format, s := c.Arguments[0].String(), make([]interface{}, l-1) 86 for i := 1; i < l; i++ { 87 s = append(s, c.Arguments[i].Export()) 88 } 89 log.Error().Msgf(format, s...) 90 return v 91 }) 92 // log.fatal output log 93 _ = logObj.Set("fatal", func(c goja.FunctionCall) goja.Value { 94 v, l := goja.Undefined(), len(c.Arguments) 95 if l == 0 { 96 return v 97 } 98 format, s := c.Arguments[0].String(), make([]interface{}, l-1) 99 for i := 1; i < l; i++ { 100 s = append(s, c.Arguments[i].Export()) 101 } 102 log.Fatal().Msgf(format, s...) 103 return v 104 }) 105 // log.panic output log 106 _ = logObj.Set("panic", func(c goja.FunctionCall) goja.Value { 107 v, l := goja.Undefined(), len(c.Arguments) 108 if l == 0 { 109 return v 110 } 111 format, s := c.Arguments[0].String(), make([]interface{}, l-1) 112 for i := 1; i < l; i++ { 113 s = append(s, c.Arguments[i].Export()) 114 } 115 log.Panic().Msgf(format, s...) 116 return v 117 }) 118 // log.log output log 119 _ = logObj.Set("log", func(c goja.FunctionCall) goja.Value { 120 v, l := goja.Undefined(), len(c.Arguments) 121 if l == 0 { 122 return v 123 } 124 format, s := c.Arguments[0].String(), make([]interface{}, l-1) 125 for i := 1; i < l; i++ { 126 s = append(s, c.Arguments[i].Export()) 127 } 128 log.Log().Msgf(format, s...) 129 return v 130 }) 131 132 r.Set("log", logObj) 133 } 134 135 // Console use console.log,dump in javascript. 136 func Console(r *goja.Runtime) { 137 consoleObj := r.NewObject() 138 139 // console.log output content 140 _ = consoleObj.Set("log", func(c goja.FunctionCall) goja.Value { 141 fmt.Printf(" console.log:") 142 for _, a := range c.Arguments { 143 if v := a.Export(); v == nil { 144 fmt.Print(" null") 145 } else { 146 fmt.Printf(" %+v", v) 147 } 148 } 149 fmt.Println() 150 return goja.Undefined() 151 }) 152 153 r.Set("console", consoleObj) 154 155 // dump output content 156 r.Set("dump", func(c goja.FunctionCall) goja.Value { 157 l := len(c.Arguments) - 1 158 fmt.Println() 159 for i, a := range c.Arguments { 160 if v := a.Export(); v == nil { 161 fmt.Print(" null") 162 } else { 163 fmt.Printf(" %+v", v) 164 } 165 if i < l { 166 fmt.Println() 167 } 168 } 169 fmt.Println() 170 return goja.Undefined() 171 }) 172 } 173 174 // ID create a new random ID in javascript. 175 // ID(): return a new random UUID. 176 // ID(9),ID(10),ID(20),ID(32),ID(36) 177 func ID(r *goja.Runtime) { 178 r.Set("ID", func(c goja.FunctionCall) goja.Value { 179 var l int64 = 36 180 if len(c.Arguments) > 0 { 181 l = c.Arguments[0].ToInteger() 182 } 183 switch l { 184 case 9: 185 return r.ToValue(id.L9()) 186 case 10: 187 return r.ToValue(id.L10()) 188 case 20: 189 return r.ToValue(id.L20()) 190 case 32: 191 return r.ToValue(id.L32()) 192 case 36: 193 return r.ToValue(id.L36()) 194 default: 195 return r.ToValue(id.L36()) 196 } 197 }) 198 } 199 200 // RD create a new random string in javascript. 201 // RD(): return a new random string. 202 // RD(3),ID(4),ID(5),ID(6),ID(7)... 203 func RD(r *goja.Runtime) { 204 r.Set("RD", func(c goja.FunctionCall) goja.Value { 205 var l int64 = 6 206 if len(c.Arguments) > 0 { 207 l = c.Arguments[0].ToInteger() 208 if l < 2 { 209 l = 2 210 } else if l > 2000 { 211 l = 2000 212 } 213 } 214 return r.ToValue(random.AlphaNumber(int(l))) 215 }) 216 } 217 218 // Db use database and execute sql in javascript. 219 // db.q: return ResultObject or Array of all rows 220 // db.q('select * from table1 where id=?',1) 221 // db.q('select * from table1 where id=:id',{id:1}) 222 // db.q2: return Cache{ Memory = 0, Redis, Default } ResultObject or Array of all rows 223 // db.q2(0,'select * from table1 where id=:id',{id:1}) 224 // db.g: return ResultValue of first column in first row 225 // db.g('select name from table1 where id=?',1) 226 // db.g('select name from table1 where id=:id',{id:1}) 227 // db.g2: return Cache{ Memory = 0, Redis, Default } ResultValue of first column in first row 228 // db.g2(0,'select name from table1 where id=:id',{id:1}) 229 // db.i: return LastInsertId must int in number-id-column 230 // db.i('insert into table1 values(?,?)',1,'test') 231 // db.i('insert into table1 values(:id,:name)',{id:1,name:'test'}) 232 // db.x: return RowsAffected all inserted,updated,deleted 233 // db.x('update table1 set name=? where id=?','test',1) 234 // db.x('update table1 set name=:name where id=:id',{id:1,name:'test'}) 235 func Db(r *goja.Runtime, d *sqlx.DB, dbs ...string) { 236 dbObj := r.NewObject() 237 238 if d == nil && len(dbs) == 2 { 239 var err error 240 dbType, dbConn := dbs[0], dbs[1] 241 if d, err = sqlx.Connect(dbType, dbConn); err != nil { 242 _, _ = fmt.Fprintf(os.Stderr, "[js.Register.Db] %v\n", err) 243 return 244 } 245 } 246 if d == nil { 247 _, _ = fmt.Fprintln(os.Stderr, "[js.Register] failed connect to db") 248 } 249 250 driver := make(map[string]interface{}) 251 driver["name"] = d.DriverName() 252 _ = dbObj.Set("driver", driver) 253 254 _ = dbObj.Set("q", func(c goja.FunctionCall) goja.Value { 255 v, l := goja.Null(), len(c.Arguments) 256 if l == 0 { 257 return v 258 } 259 260 var ( 261 sql = c.Arguments[0].String() 262 rows *sqlx.Rows 263 err error 264 value map[string]interface{} 265 hasValue bool 266 ) 267 268 if l == 2 { 269 value, hasValue = c.Arguments[1].Export().(map[string]interface{}) 270 } 271 272 if hasValue { 273 if rows, err = d.NamedQuery(sql, value); err != nil { 274 return r.ToValue(err) 275 } 276 } else { 277 values := make([]interface{}, 0, l-1) 278 if l > 1 { 279 for _, a := range c.Arguments[1:] { 280 values = append(values, a.Export()) 281 } 282 } 283 if rows, err = d.Queryx(sql, values...); err != nil { 284 return r.ToValue(err) 285 } 286 } 287 288 results := make([]map[string]interface{}, 0) 289 for rows.Next() { 290 result := make(map[string]interface{}) 291 if err = rows.MapScan(result); err != nil { 292 return r.ToValue(err) 293 } 294 for k, v := range result { 295 if s, ok := v.([]byte); ok { 296 result[k] = string(s) 297 } 298 } 299 results = append(results, result) 300 } 301 302 if len(results) == 1 { 303 v = r.ToValue(results[0]) 304 } else { 305 v = r.ToValue(results) 306 } 307 308 return v 309 }) 310 311 _ = dbObj.Set("q2", func(c goja.FunctionCall) goja.Value { 312 v, l := goja.Null(), len(c.Arguments) 313 if l <= 1 { 314 return v 315 } 316 317 var ( 318 typ = c.Arguments[0].ToInteger() 319 sql = c.Arguments[1].String() 320 rows *sqlx.Rows 321 err error 322 value map[string]interface{} 323 hasValue bool 324 ) 325 326 results := make([]map[string]interface{}, 0) 327 328 key := getCacheKeyFrom(c.Arguments) 329 val := getCacheValFrom(r, typ, key) 330 expiration := time.Hour 331 if val != nil { 332 if err := json.Unmarshal(val, &results); err != nil { 333 results = make([]map[string]interface{}, 0) 334 } 335 } 336 337 if len(results) == 0 { 338 if l == 3 { 339 value, hasValue = c.Arguments[2].Export().(map[string]interface{}) 340 } 341 342 if hasValue { 343 if rows, err = d.NamedQuery(sql, value); err != nil { 344 return r.ToValue(err) 345 } 346 } else { 347 values := make([]interface{}, 0, l-2) 348 if l > 2 { 349 for _, a := range c.Arguments[2:] { 350 values = append(values, a.Export()) 351 } 352 } 353 if rows, err = d.Queryx(sql, values...); err != nil { 354 return r.ToValue(err) 355 } 356 } 357 358 for rows.Next() { 359 result := make(map[string]interface{}) 360 if err = rows.MapScan(result); err != nil { 361 return r.ToValue(err) 362 } 363 for k, v := range result { 364 if s, ok := v.([]byte); ok { 365 result[k] = string(s) 366 } 367 } 368 results = append(results, result) 369 } 370 if len(results) > 0 { 371 setCacheValFrom(r, typ, key, results, expiration) 372 } 373 } 374 375 if len(results) == 1 { 376 v = r.ToValue(results[0]) 377 } else { 378 v = r.ToValue(results) 379 } 380 381 return v 382 }) 383 384 _ = dbObj.Set("g", func(c goja.FunctionCall) goja.Value { 385 v, l := goja.Null(), len(c.Arguments) 386 if l == 0 { 387 return v 388 } 389 390 var ( 391 sql = c.Arguments[0].String() 392 rows *sqlx.Rows 393 err error 394 value map[string]interface{} 395 hasValue bool 396 ) 397 398 if l == 2 { 399 value, hasValue = c.Arguments[1].Export().(map[string]interface{}) 400 } 401 402 if hasValue { 403 if rows, err = d.NamedQuery(sql, value); err != nil { 404 return r.ToValue(err) 405 } 406 } else { 407 values := make([]interface{}, 0, l-1) 408 if l > 1 { 409 for _, a := range c.Arguments[1:] { 410 values = append(values, a.Export()) 411 } 412 } 413 if rows, err = d.Queryx(sql, values...); err != nil { 414 return r.ToValue(err) 415 } 416 } 417 418 for rows.Next() { 419 result := make(map[string]interface{}) 420 if err = rows.MapScan(result); err != nil { 421 return r.ToValue(err) 422 } 423 for k, v := range result { 424 if s, ok := v.([]byte); ok { 425 result[k] = string(s) 426 } 427 } 428 if cols, err := rows.Columns(); err != nil || len(cols) > 1 { 429 v = r.ToValue(result) 430 } else if len(cols) > 0 { 431 v = r.ToValue(result[cols[0]]) 432 } 433 break 434 } 435 436 return v 437 }) 438 439 _ = dbObj.Set("g2", func(c goja.FunctionCall) goja.Value { 440 v, l := goja.Null(), len(c.Arguments) 441 if l <= 1 { 442 return v 443 } 444 445 var ( 446 typ = c.Arguments[0].ToInteger() 447 sql = c.Arguments[1].String() 448 rows *sqlx.Rows 449 err error 450 value map[string]interface{} 451 hasValue bool 452 ) 453 454 var one interface{} 455 result := make(map[string]interface{}) 456 457 key := getCacheKeyFrom(c.Arguments) 458 val := getCacheValFrom(r, typ, key) 459 expiration := time.Hour 460 if val != nil { 461 if val[0] == '{' { 462 if err := json.Unmarshal(val, &result); err != nil { 463 result = make(map[string]interface{}) 464 } else { 465 v = r.ToValue(result) 466 } 467 } else { 468 if err := json.Unmarshal(val, &one); err == nil { 469 v = r.ToValue(one) 470 } 471 } 472 } 473 474 if one == nil && len(result) == 0 { 475 if l == 3 { 476 value, hasValue = c.Arguments[2].Export().(map[string]interface{}) 477 } 478 479 if hasValue { 480 if rows, err = d.NamedQuery(sql, value); err != nil { 481 return r.ToValue(err) 482 } 483 } else { 484 values := make([]interface{}, 0, l-2) 485 if l > 2 { 486 for _, a := range c.Arguments[2:] { 487 values = append(values, a.Export()) 488 } 489 } 490 if rows, err = d.Queryx(sql, values...); err != nil { 491 return r.ToValue(err) 492 } 493 } 494 495 for rows.Next() { 496 if err = rows.MapScan(result); err != nil { 497 return r.ToValue(err) 498 } 499 for k, v := range result { 500 if s, ok := v.([]byte); ok { 501 result[k] = string(s) 502 } 503 } 504 if cols, err := rows.Columns(); err != nil || len(cols) > 1 { 505 setCacheValFrom(r, typ, key, result, expiration) 506 v = r.ToValue(result) 507 } else if len(cols) > 0 { 508 setCacheValFrom(r, typ, key, result[cols[0]], expiration) 509 v = r.ToValue(result[cols[0]]) 510 } 511 break 512 } 513 } 514 515 return v 516 }) 517 518 _ = dbObj.Set("i", func(c goja.FunctionCall) goja.Value { 519 v, l := r.ToValue(-1), len(c.Arguments) 520 if l == 0 { 521 return v 522 } 523 524 var ( 525 sql = c.Arguments[0].String() 526 insertID int64 527 value map[string]interface{} 528 hasValue bool 529 ) 530 531 if l == 2 { 532 value, hasValue = c.Arguments[1].Export().(map[string]interface{}) 533 } 534 535 if hasValue { 536 rows, err := d.Exec(sql, value) 537 if err != nil { 538 return r.ToValue(err) 539 } 540 insertID, _ = rows.LastInsertId() 541 } else { 542 values := make([]interface{}, 0, l-1) 543 if l > 1 { 544 for _, a := range c.Arguments[1:] { 545 values = append(values, a.Export()) 546 } 547 } 548 rows, err := d.Exec(sql, values...) 549 if err != nil { 550 return r.ToValue(err) 551 } 552 insertID, _ = rows.LastInsertId() 553 } 554 v = r.ToValue(insertID) 555 556 return v 557 }) 558 559 _ = dbObj.Set("x", func(c goja.FunctionCall) goja.Value { 560 v, l := r.ToValue(-1), len(c.Arguments) 561 if l == 0 { 562 return v 563 } 564 565 var ( 566 sql = c.Arguments[0].String() 567 affected int64 568 value map[string]interface{} 569 hasValue bool 570 ) 571 572 if l == 2 { 573 value, hasValue = c.Arguments[1].Export().(map[string]interface{}) 574 } 575 576 if hasValue { 577 rows, err := d.Exec(sql, value) 578 if err != nil { 579 return r.ToValue(err) 580 } 581 affected, _ = rows.RowsAffected() 582 } else { 583 values := make([]interface{}, 0, l-1) 584 if l > 1 { 585 for _, a := range c.Arguments[1:] { 586 values = append(values, a.Export()) 587 } 588 } 589 rows, err := d.Exec(sql, values...) 590 if err != nil { 591 return r.ToValue(err) 592 } 593 affected, _ = rows.RowsAffected() 594 } 595 v = r.ToValue(affected) 596 597 return v 598 }) 599 600 r.Set("db", dbObj) 601 } 602 603 // Nats use nats in javascript. 604 // console.log(nats.name) 605 // console.log(nats.subject) 606 // nats.pub('data'); nats.pub('subj','data') 607 // nats.req('data'); nats.pub('data',3); nats.pub('subj','data',3) // timeout:3s 608 func Nats(r *goja.Runtime, nc *nats.Conn, subj string) { 609 natsObj := r.NewObject() 610 611 _ = natsObj.Set("name", nc.Opts.Name) 612 _ = natsObj.Set("subject", subj) 613 614 _ = natsObj.Set("pub", func(c goja.FunctionCall) goja.Value { 615 v, l := goja.Null(), len(c.Arguments) 616 if l == 1 && subj != "" { 617 data := c.Arguments[0].String() 618 if err := nc.Publish(subj, f.Bytes(data)); err != nil { 619 return r.ToValue(err) 620 } 621 return r.ToValue(0) 622 } else if l == 2 { 623 subj, data := c.Arguments[0].String(), c.Arguments[1].String() 624 if err := nc.Publish(subj, f.Bytes(data)); err != nil { 625 return r.ToValue(err) 626 } 627 return r.ToValue(0) 628 } 629 return v 630 }) 631 632 _ = natsObj.Set("req", func(c goja.FunctionCall) goja.Value { 633 v, l := goja.Null(), len(c.Arguments) 634 if l == 1 && subj != "" { 635 data := c.Arguments[0].String() 636 msg, err := nc.Request(subj, f.Bytes(data), 3*time.Second) 637 if err != nil { 638 return r.ToValue(err) 639 } 640 if msg.Data == nil { 641 return v 642 } 643 return r.ToValue(string(msg.Data)) 644 } else if l == 2 && subj != "" { 645 data, ms := c.Arguments[0].String(), c.Arguments[1].ToInteger() 646 msg, err := nc.Request(subj, f.Bytes(data), time.Duration(ms)*time.Second) 647 if err != nil { 648 return r.ToValue(err) 649 } 650 if msg.Data == nil { 651 return v 652 } 653 return r.ToValue(string(msg.Data)) 654 } else if l == 3 { 655 subj, data, ms := c.Arguments[0].String(), c.Arguments[1].String(), c.Arguments[2].ToInteger() 656 msg, err := nc.Request(subj, f.Bytes(data), time.Duration(ms)*time.Second) 657 if err != nil { 658 return r.ToValue(err) 659 } 660 if msg.Data == nil { 661 return v 662 } 663 return r.ToValue(string(msg.Data)) 664 } 665 return v 666 }) 667 668 r.Set("nats", natsObj) 669 } 670 671 // Ajax use $ in javascript. 672 // dump($.header, $.user, $.trace, $.body, $.cookie, $.token) 673 // var res = $.q("get",url) 674 // var res = $.q("get",url,param) 675 // var res = $.q("post",url,param,"json") 676 // $.q("get",url,param,"",function(data,status)) 677 // $.q("post",url,param,"json",function(data,status)) 678 func Ajax(r *goja.Runtime) { 679 jObj := r.NewObject() 680 681 header := make(map[string]interface{}) 682 header["Accept"] = "*/*" 683 header["Accept-Language"] = "zh-CN,zh;q=0.9,zh;q=0.8,en;q=0.7" 684 header["User-Agent"] = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.105 Safari/537.36" 685 _ = jObj.Set("header", header) 686 687 user := make(map[string]string) 688 user["username"] = "" 689 user["password"] = "" 690 _ = jObj.Set("user", user) 691 _ = jObj.Set("body", "") 692 _ = jObj.Set("cookie", "") 693 _ = jObj.Set("token", "") 694 _ = jObj.Set("trace", false) 695 696 var trace = func(req *resty.Request, res *resty.Response, result map[string]interface{}) { 697 if jObj.Get("trace").ToBoolean() { 698 cookies := make(map[string]interface{}) 699 for _, cookie := range req.Cookies { 700 name, val := cookie.Name, cookie.Value 701 cookies[name] = val 702 } 703 dump := ` 704 ---- %s: %s 705 ---- trace: %s 706 ---- request-header: %s 707 ---- request-cookie: %s 708 ---- request-body: %s 709 ---- response-body: %s 710 ---- response-cookie: %s 711 ---- response-result: %s 712 ` 713 fmt.Printf(dump, req.Method, req.URL, f.EncodedJson(req.TraceInfo()), f.EncodedJson(jObj.Get("header").Export()), f.EncodedJson(cookies), req.Body, res.Body(), f.EncodedJson(jObj.Get("cookie").Export()), f.EncodedJson(result)) 714 } 715 } 716 717 var setReq = func(req *resty.Request, data interface{}) { 718 if jObj.Get("trace").ToBoolean() { 719 req.EnableTrace() 720 } 721 722 if tObj := jObj.Get("header").Export(); tObj != nil { 723 switch tVal := tObj.(type) { 724 case map[string]interface{}: 725 for name, val := range tVal { 726 req.SetHeader(name, f.ToString(val)) 727 } 728 case string: 729 for _, line := range strings.Split(strings.TrimSpace(tVal), "\n") { 730 if str := strings.Split(strings.TrimSpace(line), ":"); len(str) == 2 { 731 req.SetHeader(strings.TrimSpace(str[0]), strings.TrimSpace(str[1])) 732 } 733 } 734 } 735 } 736 737 if tObj := jObj.Get("cookie").Export(); tObj != nil { 738 switch tVal := tObj.(type) { 739 case map[string]interface{}: 740 for name, val := range tVal { 741 req.SetCookie(&http.Cookie{Name: name, Value: f.ToString(val)}) 742 } 743 case string: 744 for _, line := range strings.Split(strings.TrimSpace(tVal), "\n") { 745 if str := strings.Split(strings.TrimSpace(line), ":"); len(str) == 2 { 746 name, val := strings.TrimSpace(str[0]), strings.TrimSpace(str[1]) 747 req.SetCookie(&http.Cookie{Name: name, Value: val}) 748 } 749 } 750 } 751 } 752 753 if data == nil { 754 data = jObj.Get("body").Export() 755 } 756 if data != nil { 757 switch tVal := data.(type) { 758 case string: 759 if tVal != "" { 760 req.SetBody([]byte(tVal)) 761 } 762 case map[string]interface{}: 763 switch req.Header.Get("Content-Type") { 764 case "application/json": 765 if buf, err := json.Marshal(tVal); err == nil { 766 req.SetBody(buf) 767 } 768 case "application/xml": 769 if buf, err := xml.Marshal(tVal); err == nil { 770 req.SetBody(buf) 771 } 772 default: 773 items := make([]string, 0, len(tVal)) 774 for k, v := range tVal { 775 items = append(items, url.QueryEscape(k)+"="+url.QueryEscape(f.ToString(v))) 776 } 777 req.SetBody(f.Bytes(strings.Join(items, "&"))) 778 } 779 case map[interface{}]interface{}: 780 switch req.Header.Get("Content-Type") { 781 case "application/json": 782 if buf, err := json.Marshal(tVal); err == nil { 783 req.SetBody(buf) 784 } 785 case "application/xml": 786 if buf, err := xml.Marshal(tVal); err == nil { 787 req.SetBody(buf) 788 } 789 default: 790 items := make([]string, 0, len(tVal)) 791 for k, v := range tVal { 792 items = append(items, url.QueryEscape(f.ToString(k))+"="+url.QueryEscape(f.ToString(v))) 793 } 794 req.SetBody(f.Bytes(strings.Join(items, "&"))) 795 } 796 default: 797 if buf, err := json.Marshal(tVal); err == nil { 798 req.SetBody(buf) 799 } 800 } 801 } 802 } 803 804 var setRes = func(res *resty.Response) { 805 cookie := make(map[string]interface{}) 806 for _, cc := range res.Cookies() { 807 cookie[cc.Name] = cc.Value 808 } 809 _ = jObj.Set("cookie", cookie) 810 _ = jObj.Set("body", "") 811 } 812 813 _ = jObj.Set("q", func(c goja.FunctionCall) goja.Value { 814 v, l := goja.Null(), len(c.Arguments) 815 if l < 2 { 816 return v 817 } 818 819 method, urlStr := c.Arguments[0].String(), c.Arguments[1].String() 820 if method == "" || urlStr == "" { 821 return v 822 } 823 824 var fn func(map[string]interface{}, int) 825 callback := l == 5 826 if callback { 827 if err := r.ExportTo(c.Arguments[4], &fn); err != nil { 828 return r.ToValue(err) 829 } 830 } 831 832 var ( 833 cont string 834 data interface{} 835 req = ht.NewRestRequest() 836 ) 837 if l > 3 { 838 data = c.Arguments[2].Export() 839 if err := r.ExportTo(c.Arguments[3], &fn); err == nil { 840 callback = true 841 } else { 842 cont = c.Arguments[3].String() 843 } 844 } else if l > 2 { 845 if err := r.ExportTo(c.Arguments[2], &fn); err == nil { 846 callback = true 847 } else { 848 data = c.Arguments[2].Export() 849 } 850 } 851 852 if strings.Contains(cont, "json") { 853 req.SetHeader("Content-Type", "application/json") 854 } else if strings.Contains(cont, "xml") { 855 req.SetHeader("Content-Type", "application/xml") 856 } else if strings.Contains(cont, "form") || strings.Contains(cont, "url") { 857 req.SetHeader("Content-Type", "application/x-www-form-urlencoded") 858 } else if strings.Contains(cont, "file") || strings.Contains(cont, "data") { 859 req.SetHeader("Content-Type", "multipart/form-data") 860 } else if strings.Contains(cont, "text") { 861 req.SetHeader("Content-Type", "text/plain") 862 } else if len(cont) > 10 { 863 req.SetHeader("Content-Type", cont) 864 } 865 866 if token := jObj.Get("token").String(); token != "" { 867 req.SetAuthToken(token) 868 } 869 870 if user, ok := jObj.Get("user").Export().(map[string]string); ok && user != nil && user["username"] != "" { 871 req.SetBasicAuth(user["username"], user["password"]) 872 } 873 874 setReq(req, data) 875 method = strings.ToUpper(method) 876 result := make(map[string]interface{}) 877 res, err := req.Execute(method, urlStr) 878 if err != nil { 879 result["error"] = err.Error() 880 result["code"] = -1 881 result["data"] = nil 882 // request did not occur and could not be traced 883 // trace(req, res, result) 884 if callback { 885 fn(result, -1) 886 return v 887 } 888 return r.ToValue(result) 889 } 890 891 statusCode := res.StatusCode() 892 result["error"] = res.Status() 893 result["code"] = statusCode 894 result["data"] = nil 895 setRes(res) 896 buf := res.Body() 897 if buf == nil || statusCode >= 400 { 898 trace(req, res, result) 899 if callback { 900 fn(result, statusCode) 901 return v 902 } 903 return r.ToValue(result) 904 } 905 906 buf = bytes.TrimSpace(buf) 907 st0, st1 := buf[0], buf[len(buf)-1] 908 if st0 == '[' && st1 == ']' { 909 var records []map[string]interface{} 910 if err := json.Unmarshal(buf, &records); err == nil { 911 result["data"] = records 912 } else { 913 result["data"] = f.String(buf) 914 } 915 } else if st0 == '{' || st1 == '}' { 916 var record map[string]interface{} 917 if err := json.Unmarshal(buf, &record); err == nil { 918 result["data"] = record 919 } else { 920 result["data"] = f.String(buf) 921 } 922 } else { 923 result["data"] = f.String(buf) 924 } 925 926 result["error"] = nil 927 trace(req, res, result) 928 if callback { 929 fn(result, statusCode) 930 return v 931 } 932 return r.ToValue(result) 933 }) 934 935 r.Set("$", jObj) 936 } 937 938 // Cache use cache in javascript. 939 // dump(cache.dir, cache.cap) 940 // var val = cache.get("key") 941 // var has = cache.has("key") 942 // cache.set("key",123) 943 // cache.del("key") 944 // cache.reset(); cache.clear(); cache.clear('cache-01'); 945 // try { cache.save('cache-01'); cache.load('cache-01'); } catch (e) { throw(e) } 946 func Cache(r *goja.Runtime, cache *fastcache.Cache, cacheDir string, maxBytes ...int) { 947 var err error 948 // default directory 949 currentDir := f.CurrentDir() 950 defaultDir := filepath.Join(currentDir, ".nats") 951 if cacheDir == "" { 952 cacheDir = defaultDir 953 } 954 // creates a fast cache instance 955 capacity := 1073741824 // 1GB cache capacity 956 if cache == nil { 957 if len(maxBytes) > 0 { 958 capacity = maxBytes[0] 959 } 960 cache = fastcache.New(capacity) 961 } 962 963 cObj := r.NewObject() 964 965 _ = cObj.Set("dir", cacheDir) 966 _ = cObj.Set("cap", capacity) 967 968 _ = cObj.Set("get", func(c goja.FunctionCall) goja.Value { 969 v, l := goja.Null(), len(c.Arguments) 970 if l < 1 { 971 return v 972 } 973 key := c.Arguments[0].String() 974 if key == "" { 975 return v 976 } 977 978 p := cache.Get(nil, f.Bytes(key)) 979 if p == nil || len(p) == 0 { 980 return v 981 } 982 983 var val interface{} 984 if err := json.Unmarshal(p, &val); err == nil { 985 v = r.ToValue(val) 986 } 987 988 return v 989 }) 990 991 _ = cObj.Set("set", func(c goja.FunctionCall) goja.Value { 992 v, l := goja.Null(), len(c.Arguments) 993 if l < 2 { 994 return v 995 } 996 key := c.Arguments[0].String() 997 if key == "" { 998 return v 999 } 1000 1001 val := c.Arguments[1].Export() 1002 if p, err := json.Marshal(val); err != nil { 1003 cache.Set(f.Bytes(key), []byte{}) 1004 } else { 1005 cache.Set(f.Bytes(key), p) 1006 } 1007 1008 return v 1009 }) 1010 1011 _ = cObj.Set("del", func(c goja.FunctionCall) goja.Value { 1012 v, l := goja.Undefined(), len(c.Arguments) 1013 if l < 1 { 1014 return v 1015 } 1016 key := c.Arguments[0].String() 1017 if key == "" { 1018 return v 1019 } 1020 1021 cache.Del(f.Bytes(key)) 1022 1023 return v 1024 }) 1025 1026 _ = cObj.Set("has", func(c goja.FunctionCall) goja.Value { 1027 v, l := r.ToValue(false), len(c.Arguments) 1028 if l < 1 { 1029 return v 1030 } 1031 key := c.Arguments[0].String() 1032 if key == "" { 1033 return v 1034 } 1035 1036 p := cache.Has(f.Bytes(key)) 1037 v = r.ToValue(p) 1038 1039 return v 1040 }) 1041 1042 _ = cObj.Set("reset", func(c goja.FunctionCall) goja.Value { 1043 cache.Reset() 1044 return goja.Undefined() 1045 }) 1046 _ = cObj.Set("clear", func(c goja.FunctionCall) goja.Value { 1047 cache.Reset() 1048 1049 l, dir := len(c.Arguments), "" 1050 if l > 0 { 1051 dir = filepath.Join(currentDir, c.Arguments[0].String()) 1052 } 1053 if dir == "" { 1054 dir = cacheDir 1055 } 1056 if f.IsDir(dir) { 1057 if err := os.RemoveAll(dir); err != nil { 1058 panic(err.Error()) 1059 } 1060 } 1061 return goja.Undefined() 1062 }) 1063 1064 _ = cObj.Set("load", func(c goja.FunctionCall) goja.Value { 1065 l, dir := len(c.Arguments), "" 1066 if l > 0 { 1067 dir = filepath.Join(currentDir, c.Arguments[0].String()) 1068 } 1069 if dir == "" { 1070 dir = cacheDir 1071 if f.PathExists(dir) == false { 1072 return goja.Undefined() 1073 } 1074 } 1075 if f.PathExists(dir) == false { 1076 panic("The specified directory does not exist") 1077 } 1078 if f.PathExists(filepath.Join(dir, "metadata.bin")) == false { 1079 return goja.Undefined() 1080 } 1081 if cache, err = fastcache.LoadFromFile(dir); err != nil { 1082 panic(err.Error()) 1083 } 1084 return goja.Undefined() 1085 }) 1086 1087 _ = cObj.Set("save", func(c goja.FunctionCall) goja.Value { 1088 l, dir := len(c.Arguments), "" 1089 if l > 0 { 1090 dir = filepath.Join(currentDir, c.Arguments[0].String()) 1091 // creates a new directory if does not exist 1092 if err = f.Mkdir(dir); err != nil { 1093 panic(err.Error()) 1094 } 1095 } 1096 if dir == "" { 1097 dir = cacheDir 1098 // creates a new directory if does not exist 1099 if err = f.Mkdir(dir); err != nil { 1100 panic(err.Error()) 1101 } 1102 } 1103 if f.PathExists(dir) == false { 1104 panic("The specified directory does not exist") 1105 } 1106 if err = cache.SaveToFileConcurrent(dir, 0); err != nil { 1107 panic(err.Error()) 1108 } 1109 return goja.Undefined() 1110 }) 1111 1112 r.Set("cache", cObj) 1113 setCacheClient(r, cache) 1114 } 1115 1116 // Redis use redis in javascript. 1117 // redis.get(key) 1118 // redis.del(key,key1,key2) 1119 // redis.set(key,value,86400) // 1 days 1120 // redis.setNX(key,value,86400) 1121 // redis.incr(key), incr(key,2) 1122 // redis.lpush(key,1,2,3) 1123 // redis.rpush(key,1,2,3) 1124 // redis.sort(key,0,10,'asc') 1125 // redis.list(key,0,10) 1126 // redis.do('SET', key, value) 1127 // redis.eval('...') 1128 // http://www.runoob.com/redis/redis-tutorial.html 1129 func Redis(r *goja.Runtime, client *redis.Client) { 1130 rObj := r.NewObject() 1131 1132 // GET key 1133 _ = rObj.Set("get", func(c goja.FunctionCall) goja.Value { 1134 v, l := goja.Null(), len(c.Arguments) 1135 if l < 1 { 1136 return v 1137 } 1138 1139 res, err := client.Get(c.Arguments[0].String()).Result() 1140 if err == redis.Nil { 1141 return v 1142 } else if err != nil { 1143 return r.ToValue(err) 1144 } 1145 1146 return r.ToValue(res) 1147 }) 1148 1149 // TTL key 1150 _ = rObj.Set("ttl", func(c goja.FunctionCall) goja.Value { 1151 v, l := goja.Null(), len(c.Arguments) 1152 if l < 1 { 1153 return v 1154 } 1155 1156 res, err := client.TTL(c.Arguments[0].String()).Result() 1157 if err == redis.Nil { 1158 return v 1159 } else if err != nil { 1160 return r.ToValue(err) 1161 } 1162 1163 return r.ToValue(res) 1164 }) 1165 1166 // DEL key 1167 _ = rObj.Set("del", func(c goja.FunctionCall) goja.Value { 1168 v, l := goja.Null(), len(c.Arguments) 1169 if l < 1 { 1170 return v 1171 } 1172 1173 args := make([]string, 0, l) 1174 for _, a := range c.Arguments { 1175 args = append(args, a.String()) 1176 } 1177 res, err := client.Del(args...).Result() 1178 if err == redis.Nil { 1179 return v 1180 } else if err != nil { 1181 return r.ToValue(err) 1182 } 1183 1184 return r.ToValue(res) 1185 }) 1186 1187 // SET key value EX 10 1188 _ = rObj.Set("set", func(c goja.FunctionCall) goja.Value { 1189 v, l := goja.Null(), len(c.Arguments) 1190 if l < 3 { 1191 return v 1192 } 1193 1194 res, err := client.Set(c.Arguments[0].String(), c.Arguments[1].Export(), time.Duration(c.Arguments[2].ToInteger())*time.Second).Result() 1195 if err != nil { 1196 return r.ToValue(err) 1197 } 1198 1199 return r.ToValue(res) 1200 }) 1201 1202 // SET key value EX 10 NX 1203 _ = rObj.Set("setNX", func(c goja.FunctionCall) goja.Value { 1204 v, l := goja.Null(), len(c.Arguments) 1205 if l < 3 { 1206 return v 1207 } 1208 1209 res, err := client.SetNX(c.Arguments[0].String(), c.Arguments[1].Export(), time.Duration(c.Arguments[2].ToInteger())*time.Second).Result() 1210 if err != nil { 1211 return r.ToValue(err) 1212 } 1213 1214 return r.ToValue(res) 1215 }) 1216 1217 // INCR key, IncrBy key 10 1218 _ = rObj.Set("incr", func(c goja.FunctionCall) goja.Value { 1219 v, l := goja.Null(), len(c.Arguments) 1220 if l < 1 { 1221 return v 1222 } 1223 1224 if l == 1 { 1225 res, err := client.Incr(c.Arguments[0].String()).Result() 1226 if err != nil { 1227 return r.ToValue(err) 1228 } 1229 return r.ToValue(res) 1230 } 1231 1232 res, err := client.IncrBy(c.Arguments[0].String(), c.Arguments[1].ToInteger()).Result() 1233 if err != nil { 1234 return r.ToValue(err) 1235 } 1236 return r.ToValue(res) 1237 }) 1238 1239 // LPUSH list 1 10 100 1240 _ = rObj.Set("lpush", func(c goja.FunctionCall) goja.Value { 1241 v, l := goja.Null(), len(c.Arguments) 1242 if l < 2 { 1243 return v 1244 } 1245 1246 args := make([]interface{}, 0, l-1) 1247 for i, a := range c.Arguments { 1248 if i == 0 { 1249 continue 1250 } 1251 args = append(args, a.Export()) 1252 } 1253 res, err := client.LPush(c.Arguments[0].String(), args...).Result() 1254 if err != nil { 1255 return r.ToValue(err) 1256 } 1257 1258 return r.ToValue(res) 1259 }) 1260 1261 // RPUSH list 1 10 100 1262 _ = rObj.Set("rpush", func(c goja.FunctionCall) goja.Value { 1263 v, l := goja.Null(), len(c.Arguments) 1264 if l < 2 { 1265 return v 1266 } 1267 1268 args := make([]interface{}, 0, l-1) 1269 for i, a := range c.Arguments { 1270 if i == 0 { 1271 continue 1272 } 1273 args = append(args, a.Export()) 1274 } 1275 res, err := client.RPush(c.Arguments[0].String(), args...).Result() 1276 if err != nil { 1277 return r.ToValue(err) 1278 } 1279 1280 return r.ToValue(res) 1281 }) 1282 1283 // SORT list LIMIT 0 2 ASC 1284 _ = rObj.Set("sort", func(c goja.FunctionCall) goja.Value { 1285 v, l := goja.Null(), len(c.Arguments) 1286 if l < 4 { 1287 return v 1288 } 1289 1290 sort := redis.Sort{Offset: c.Arguments[1].ToInteger(), Count: c.Arguments[2].ToInteger(), Order: strings.ToUpper(c.Arguments[3].String())} 1291 res, err := client.Sort(c.Arguments[0].String(), &sort).Result() 1292 if err == redis.Nil { 1293 return v 1294 } else if err != nil { 1295 return r.ToValue(err) 1296 } 1297 1298 return r.ToValue(res) 1299 }) 1300 1301 // GetRange list 0 10 1302 _ = rObj.Set("list", func(c goja.FunctionCall) goja.Value { 1303 v, l := goja.Null(), len(c.Arguments) 1304 if l < 3 { 1305 return v 1306 } 1307 1308 res, err := client.GetRange(c.Arguments[0].String(), c.Arguments[1].ToInteger(), c.Arguments[2].ToInteger()).Result() 1309 if err == redis.Nil { 1310 return v 1311 } else if err != nil { 1312 return r.ToValue(err) 1313 } 1314 1315 return r.ToValue(res) 1316 }) 1317 1318 _ = rObj.Set("do", func(c goja.FunctionCall) goja.Value { 1319 v, l := goja.Null(), len(c.Arguments) 1320 if l < 2 { 1321 return v 1322 } 1323 1324 args := make([]interface{}, 0, l) 1325 for _, a := range c.Arguments { 1326 args = append(args, a.Export()) 1327 } 1328 1329 res, err := client.Do(args...).Result() 1330 if err == redis.Nil { 1331 return v 1332 } else if err != nil { 1333 return r.ToValue(err) 1334 } 1335 1336 return r.ToValue(res) 1337 }) 1338 1339 _ = rObj.Set("eval", func(c goja.FunctionCall) goja.Value { 1340 v, l := goja.Null(), len(c.Arguments) 1341 if l < 2 { 1342 return v 1343 } 1344 1345 var script string 1346 keys, args := make([]string, 0, l-1), make([]interface{}, 0, l-2) 1347 for i, a := range c.Arguments { 1348 if i == 0 { 1349 script = a.String() 1350 continue 1351 } 1352 switch tVal := a.Export().(type) { 1353 case string: 1354 keys = append(keys, tVal) 1355 default: 1356 args = append(args, tVal) 1357 } 1358 } 1359 if script == "" { 1360 return v 1361 } 1362 1363 res, err := client.Eval(script, keys, args...).Result() 1364 if err == redis.Nil { 1365 return v 1366 } else if err != nil { 1367 return r.ToValue(err) 1368 } 1369 1370 return r.ToValue(res) 1371 }) 1372 1373 r.Set("redis", rObj) 1374 setRedisClient(r, client) 1375 } 1376 1377 // getCacheValFrom Cache{ Memory = 0, Redis, Default } 1378 func getCacheValFrom(r *goja.Runtime, typ int64, key []byte) []byte { 1379 if typ == 0 { 1380 if c := getCacheClient(r); c != nil { 1381 val := c.Get(nil, key) 1382 if val == nil || len(val) == 0 { 1383 return nil 1384 } 1385 return val 1386 } 1387 } else if typ == 1 { 1388 if c := getRedisClient(r); c != nil { 1389 val, err := c.Get(f.String(key)).Result() 1390 if err != nil { 1391 return nil 1392 } 1393 return f.Bytes(val) 1394 } 1395 } else if typ == 2 { 1396 if c := getCacheClient(r); c != nil { 1397 val := c.Get(nil, key) 1398 if val == nil || len(val) == 0 { 1399 if c := getRedisClient(r); c != nil { 1400 val, err := c.Get(f.String(key)).Result() 1401 if err != nil { 1402 return nil 1403 } 1404 return f.Bytes(val) 1405 } 1406 } 1407 return val 1408 } 1409 if c := getRedisClient(r); c != nil { 1410 val, err := c.Get(f.String(key)).Result() 1411 if err != nil { 1412 return nil 1413 } 1414 return f.Bytes(val) 1415 } 1416 } 1417 return nil 1418 } 1419 1420 // setCacheValFrom Cache{ Memory = 0, Redis, Default } 1421 func setCacheValFrom(r *goja.Runtime, typ int64, key []byte, value interface{}, expiration time.Duration) { 1422 p, err := json.Marshal(value) 1423 if err != nil { 1424 return 1425 } 1426 if typ == 0 { 1427 if c := getCacheClient(r); c != nil { 1428 c.Set(key, p) 1429 } 1430 } else if typ == 1 { 1431 if c := getRedisClient(r); c != nil { 1432 _ = c.Set(f.String(key), p, expiration).Err() 1433 } 1434 } else if typ == 2 { 1435 if c := getCacheClient(r); c != nil { 1436 c.Set(key, p) 1437 } 1438 if c := getRedisClient(r); c != nil { 1439 _ = c.Set(f.String(key), p, expiration).Err() 1440 } 1441 } 1442 } 1443 1444 func getCacheKeyFrom(args []goja.Value) []byte { 1445 data := make([]byte, 0, 256) 1446 for _, obj := range args[1:] { 1447 if goja.IsUndefined(obj) || goja.IsNull(obj) { 1448 //data = append(data, ' ') 1449 continue 1450 } 1451 v := obj.Export() 1452 if p, err := json.Marshal(v); err == nil { 1453 data = append(data, p...) 1454 } 1455 } 1456 data = f.BytesUint32(crc32.ChecksumIEEE(data)) 1457 return data 1458 } 1459 1460 func setCacheClient(r *goja.Runtime, client *fastcache.Cache) { 1461 r.Set("_cache", client) 1462 } 1463 1464 func getCacheClient(r *goja.Runtime) *fastcache.Cache { 1465 obj := r.Get("_cache") 1466 if obj == nil || goja.IsUndefined(obj) || goja.IsNull(obj) { 1467 return nil 1468 } 1469 if client, ok := obj.Export().(*fastcache.Cache); ok { 1470 return client 1471 } 1472 return nil 1473 } 1474 1475 func setRedisClient(r *goja.Runtime, client *redis.Client) { 1476 r.Set("_redis", client) 1477 } 1478 1479 func getRedisClient(r *goja.Runtime) *redis.Client { 1480 obj := r.Get("_redis") 1481 if obj == nil || goja.IsUndefined(obj) || goja.IsNull(obj) { 1482 return nil 1483 } 1484 if client, ok := obj.Export().(*redis.Client); ok { 1485 return client 1486 } 1487 return nil 1488 }