| 
									
										
										
										
											2022-11-26 09:05:52 +02:00
										 |  |  | package jsvm | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							| 
									
										
										
										
											2023-07-11 18:09:55 +03:00
										 |  |  | 	"bytes" | 
					
						
							|  |  |  | 	"context" | 
					
						
							| 
									
										
										
										
											2022-11-26 09:05:52 +02:00
										 |  |  | 	"encoding/json" | 
					
						
							| 
									
										
										
										
											2023-07-08 13:51:16 +03:00
										 |  |  | 	"errors" | 
					
						
							| 
									
										
										
										
											2023-07-11 18:09:55 +03:00
										 |  |  | 	"io" | 
					
						
							| 
									
										
										
										
											2023-11-26 13:33:17 +02:00
										 |  |  | 	"log/slog" | 
					
						
							| 
									
										
										
										
											2023-07-11 18:09:55 +03:00
										 |  |  | 	"net/http" | 
					
						
							| 
									
										
										
										
											2023-07-24 13:59:13 +03:00
										 |  |  | 	"os" | 
					
						
							|  |  |  | 	"os/exec" | 
					
						
							| 
									
										
										
										
											2023-07-24 16:39:11 +03:00
										 |  |  | 	"path/filepath" | 
					
						
							| 
									
										
										
										
											2022-11-30 17:23:00 +02:00
										 |  |  | 	"reflect" | 
					
						
							| 
									
										
										
										
											2023-07-08 13:51:16 +03:00
										 |  |  | 	"strings" | 
					
						
							| 
									
										
										
										
											2023-07-11 18:09:55 +03:00
										 |  |  | 	"time" | 
					
						
							| 
									
										
										
										
											2022-11-26 09:05:52 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	"github.com/dop251/goja" | 
					
						
							| 
									
										
										
										
											2023-06-08 17:59:08 +03:00
										 |  |  | 	validation "github.com/go-ozzo/ozzo-validation/v4" | 
					
						
							|  |  |  | 	"github.com/labstack/echo/v5" | 
					
						
							| 
									
										
										
										
											2022-11-26 09:05:52 +02:00
										 |  |  | 	"github.com/pocketbase/dbx" | 
					
						
							|  |  |  | 	"github.com/pocketbase/pocketbase/apis" | 
					
						
							| 
									
										
										
										
											2023-07-08 13:51:16 +03:00
										 |  |  | 	"github.com/pocketbase/pocketbase/core" | 
					
						
							| 
									
										
										
										
											2022-11-26 09:05:52 +02:00
										 |  |  | 	"github.com/pocketbase/pocketbase/daos" | 
					
						
							| 
									
										
										
										
											2023-06-08 17:59:08 +03:00
										 |  |  | 	"github.com/pocketbase/pocketbase/forms" | 
					
						
							| 
									
										
										
										
											2023-08-17 20:50:00 +03:00
										 |  |  | 	"github.com/pocketbase/pocketbase/mails" | 
					
						
							| 
									
										
										
										
											2022-11-26 09:05:52 +02:00
										 |  |  | 	"github.com/pocketbase/pocketbase/models" | 
					
						
							| 
									
										
										
										
											2022-11-27 23:00:58 +02:00
										 |  |  | 	"github.com/pocketbase/pocketbase/models/schema" | 
					
						
							| 
									
										
										
										
											2023-06-08 17:59:08 +03:00
										 |  |  | 	"github.com/pocketbase/pocketbase/tokens" | 
					
						
							| 
									
										
										
										
											2023-07-08 13:51:16 +03:00
										 |  |  | 	"github.com/pocketbase/pocketbase/tools/cron" | 
					
						
							| 
									
										
										
										
											2023-06-08 17:59:08 +03:00
										 |  |  | 	"github.com/pocketbase/pocketbase/tools/filesystem" | 
					
						
							| 
									
										
										
										
											2023-07-08 13:51:16 +03:00
										 |  |  | 	"github.com/pocketbase/pocketbase/tools/hook" | 
					
						
							| 
									
										
										
										
											2023-06-22 21:54:39 +03:00
										 |  |  | 	"github.com/pocketbase/pocketbase/tools/inflector" | 
					
						
							| 
									
										
										
										
											2023-07-08 13:51:16 +03:00
										 |  |  | 	"github.com/pocketbase/pocketbase/tools/list" | 
					
						
							| 
									
										
										
										
											2023-06-08 17:59:08 +03:00
										 |  |  | 	"github.com/pocketbase/pocketbase/tools/mailer" | 
					
						
							| 
									
										
										
										
											2023-09-10 10:46:19 +03:00
										 |  |  | 	"github.com/pocketbase/pocketbase/tools/rest" | 
					
						
							| 
									
										
										
										
											2023-06-08 17:59:08 +03:00
										 |  |  | 	"github.com/pocketbase/pocketbase/tools/security" | 
					
						
							| 
									
										
										
										
											2023-10-07 16:11:31 +03:00
										 |  |  | 	"github.com/pocketbase/pocketbase/tools/subscriptions" | 
					
						
							| 
									
										
										
										
											2023-06-21 11:21:40 +03:00
										 |  |  | 	"github.com/pocketbase/pocketbase/tools/types" | 
					
						
							| 
									
										
										
										
											2023-06-21 20:36:57 +03:00
										 |  |  | 	"github.com/spf13/cobra" | 
					
						
							| 
									
										
										
										
											2022-11-26 09:05:52 +02:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-08 13:51:16 +03:00
										 |  |  | // hooksBinds adds wrapped "on*" hook methods by reflecting on core.App. | 
					
						
							|  |  |  | func hooksBinds(app core.App, loader *goja.Runtime, executors *vmsPool) { | 
					
						
							|  |  |  | 	fm := FieldMapper{} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	appType := reflect.TypeOf(app) | 
					
						
							|  |  |  | 	appValue := reflect.ValueOf(app) | 
					
						
							|  |  |  | 	totalMethods := appType.NumMethod() | 
					
						
							|  |  |  | 	excludeHooks := []string{"OnBeforeServe"} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for i := 0; i < totalMethods; i++ { | 
					
						
							|  |  |  | 		method := appType.Method(i) | 
					
						
							|  |  |  | 		if !strings.HasPrefix(method.Name, "On") || list.ExistInSlice(method.Name, excludeHooks) { | 
					
						
							|  |  |  | 			continue // not a hook or excluded | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		jsName := fm.MethodName(appType, method) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// register the hook to the loader | 
					
						
							|  |  |  | 		loader.Set(jsName, func(callback string, tags ...string) { | 
					
						
							|  |  |  | 			pr := goja.MustCompile("", "{("+callback+").apply(undefined, __args)}", true) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			tagsAsValues := make([]reflect.Value, len(tags)) | 
					
						
							|  |  |  | 			for i, tag := range tags { | 
					
						
							|  |  |  | 				tagsAsValues[i] = reflect.ValueOf(tag) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			hookInstance := appValue.MethodByName(method.Name).Call(tagsAsValues)[0] | 
					
						
							|  |  |  | 			addFunc := hookInstance.MethodByName("Add") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			handlerType := addFunc.Type().In(0) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			handler := reflect.MakeFunc(handlerType, func(args []reflect.Value) (results []reflect.Value) { | 
					
						
							|  |  |  | 				handlerArgs := make([]any, len(args)) | 
					
						
							|  |  |  | 				for i, arg := range args { | 
					
						
							|  |  |  | 					handlerArgs[i] = arg.Interface() | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				err := executors.run(func(executor *goja.Runtime) error { | 
					
						
							|  |  |  | 					executor.Set("__args", handlerArgs) | 
					
						
							|  |  |  | 					res, err := executor.RunProgram(pr) | 
					
						
							|  |  |  | 					executor.Set("__args", goja.Undefined()) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-14 16:50:35 +03:00
										 |  |  | 					// check for returned error or false | 
					
						
							| 
									
										
										
										
											2023-07-08 13:51:16 +03:00
										 |  |  | 					if res != nil { | 
					
						
							| 
									
										
										
										
											2023-07-18 12:33:18 +03:00
										 |  |  | 						switch v := res.Export().(type) { | 
					
						
							|  |  |  | 						case error: | 
					
						
							|  |  |  | 							return v | 
					
						
							|  |  |  | 						case bool: | 
					
						
							|  |  |  | 							if !v { | 
					
						
							|  |  |  | 								return hook.StopPropagation | 
					
						
							| 
									
										
										
										
											2023-07-11 18:09:55 +03:00
										 |  |  | 							} | 
					
						
							| 
									
										
										
										
											2023-07-08 13:51:16 +03:00
										 |  |  | 						} | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 					return err | 
					
						
							|  |  |  | 				}) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				return []reflect.Value{reflect.ValueOf(&err).Elem()} | 
					
						
							|  |  |  | 			}) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// register the wrapped hook handler | 
					
						
							|  |  |  | 			addFunc.Call([]reflect.Value{handler}) | 
					
						
							|  |  |  | 		}) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func cronBinds(app core.App, loader *goja.Runtime, executors *vmsPool) { | 
					
						
							| 
									
										
										
										
											2023-07-16 23:24:10 +03:00
										 |  |  | 	scheduler := cron.New() | 
					
						
							| 
									
										
										
										
											2023-07-08 13:51:16 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-31 17:48:26 +03:00
										 |  |  | 	var wasServeTriggered bool | 
					
						
							| 
									
										
										
										
											2023-07-31 14:12:54 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-08 13:51:16 +03:00
										 |  |  | 	loader.Set("cronAdd", func(jobId, cronExpr, handler string) { | 
					
						
							|  |  |  | 		pr := goja.MustCompile("", "{("+handler+").apply(undefined)}", true) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-16 23:24:10 +03:00
										 |  |  | 		err := scheduler.Add(jobId, cronExpr, func() { | 
					
						
							| 
									
										
										
										
											2023-09-01 11:16:28 +03:00
										 |  |  | 			err := executors.run(func(executor *goja.Runtime) error { | 
					
						
							| 
									
										
										
										
											2023-07-08 13:51:16 +03:00
										 |  |  | 				_, err := executor.RunProgram(pr) | 
					
						
							|  |  |  | 				return err | 
					
						
							|  |  |  | 			}) | 
					
						
							| 
									
										
										
										
											2023-09-01 11:16:28 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-26 13:33:17 +02:00
										 |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				app.Logger().Debug( | 
					
						
							|  |  |  | 					"[cronAdd] failed to execute cron job", | 
					
						
							|  |  |  | 					slog.String("jobId", jobId), | 
					
						
							|  |  |  | 					slog.String("error", err.Error()), | 
					
						
							|  |  |  | 				) | 
					
						
							| 
									
										
										
										
											2023-09-01 11:16:28 +03:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2023-07-08 13:51:16 +03:00
										 |  |  | 		}) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			panic("[cronAdd] failed to register cron job " + jobId + ": " + err.Error()) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// start the ticker (if not already) | 
					
						
							| 
									
										
										
										
											2023-07-31 17:48:26 +03:00
										 |  |  | 		if wasServeTriggered && scheduler.Total() > 0 && !scheduler.HasStarted() { | 
					
						
							| 
									
										
										
										
											2023-07-16 23:24:10 +03:00
										 |  |  | 			scheduler.Start() | 
					
						
							| 
									
										
										
										
											2023-07-08 13:51:16 +03:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	loader.Set("cronRemove", func(jobId string) { | 
					
						
							| 
									
										
										
										
											2023-07-16 23:24:10 +03:00
										 |  |  | 		scheduler.Remove(jobId) | 
					
						
							| 
									
										
										
										
											2023-07-08 13:51:16 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		// stop the ticker if there are no other jobs | 
					
						
							| 
									
										
										
										
											2023-07-16 23:24:10 +03:00
										 |  |  | 		if scheduler.Total() == 0 { | 
					
						
							|  |  |  | 			scheduler.Stop() | 
					
						
							| 
									
										
										
										
											2023-07-08 13:51:16 +03:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	}) | 
					
						
							| 
									
										
										
										
											2023-07-16 23:24:10 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-31 14:12:54 +03:00
										 |  |  | 	app.OnBeforeServe().Add(func(e *core.ServeEvent) error { | 
					
						
							| 
									
										
										
										
											2023-07-16 23:24:10 +03:00
										 |  |  | 		// start the ticker (if not already) | 
					
						
							|  |  |  | 		if scheduler.Total() > 0 && !scheduler.HasStarted() { | 
					
						
							|  |  |  | 			scheduler.Start() | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-31 17:48:26 +03:00
										 |  |  | 		wasServeTriggered = true | 
					
						
							| 
									
										
										
										
											2023-07-31 14:12:54 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-16 23:24:10 +03:00
										 |  |  | 		return nil | 
					
						
							|  |  |  | 	}) | 
					
						
							| 
									
										
										
										
											2023-07-08 13:51:16 +03:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func routerBinds(app core.App, loader *goja.Runtime, executors *vmsPool) { | 
					
						
							| 
									
										
										
										
											2023-07-24 21:11:55 +03:00
										 |  |  | 	loader.Set("routerAdd", func(method string, path string, handler goja.Value, middlewares ...goja.Value) { | 
					
						
							| 
									
										
										
										
											2023-07-08 13:51:16 +03:00
										 |  |  | 		wrappedMiddlewares, err := wrapMiddlewares(executors, middlewares...) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			panic("[routerAdd] failed to wrap middlewares: " + err.Error()) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-24 21:11:55 +03:00
										 |  |  | 		wrappedHandler, err := wrapHandler(executors, handler) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			panic("[routerAdd] failed to wrap handler: " + err.Error()) | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2023-07-08 13:51:16 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		app.OnBeforeServe().Add(func(e *core.ServeEvent) error { | 
					
						
							| 
									
										
										
										
											2023-07-24 21:11:55 +03:00
										 |  |  | 			e.Router.Add(strings.ToUpper(method), path, wrappedHandler, wrappedMiddlewares...) | 
					
						
							| 
									
										
										
										
											2023-07-08 13:51:16 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			return nil | 
					
						
							|  |  |  | 		}) | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	loader.Set("routerUse", func(middlewares ...goja.Value) { | 
					
						
							|  |  |  | 		wrappedMiddlewares, err := wrapMiddlewares(executors, middlewares...) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			panic("[routerUse] failed to wrap middlewares: " + err.Error()) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		app.OnBeforeServe().Add(func(e *core.ServeEvent) error { | 
					
						
							|  |  |  | 			e.Router.Use(wrappedMiddlewares...) | 
					
						
							|  |  |  | 			return nil | 
					
						
							|  |  |  | 		}) | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	loader.Set("routerPre", func(middlewares ...goja.Value) { | 
					
						
							|  |  |  | 		wrappedMiddlewares, err := wrapMiddlewares(executors, middlewares...) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			panic("[routerPre] failed to wrap middlewares: " + err.Error()) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		app.OnBeforeServe().Add(func(e *core.ServeEvent) error { | 
					
						
							|  |  |  | 			e.Router.Pre(wrappedMiddlewares...) | 
					
						
							|  |  |  | 			return nil | 
					
						
							|  |  |  | 		}) | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-24 21:11:55 +03:00
										 |  |  | func wrapHandler(executors *vmsPool, handler goja.Value) (echo.HandlerFunc, error) { | 
					
						
							| 
									
										
										
										
											2023-07-29 16:01:38 +03:00
										 |  |  | 	if handler == nil { | 
					
						
							|  |  |  | 		return nil, errors.New("handler must be non-nil") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-24 21:11:55 +03:00
										 |  |  | 	switch h := handler.Export().(type) { | 
					
						
							|  |  |  | 	case echo.HandlerFunc: | 
					
						
							|  |  |  | 		// "native" handler - no need to wrap | 
					
						
							|  |  |  | 		return h, nil | 
					
						
							|  |  |  | 	case func(goja.FunctionCall) goja.Value, string: | 
					
						
							|  |  |  | 		pr := goja.MustCompile("", "{("+handler.String()+").apply(undefined, __args)}", true) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		wrappedHandler := func(c echo.Context) error { | 
					
						
							|  |  |  | 			return executors.run(func(executor *goja.Runtime) error { | 
					
						
							|  |  |  | 				executor.Set("__args", []any{c}) | 
					
						
							|  |  |  | 				res, err := executor.RunProgram(pr) | 
					
						
							|  |  |  | 				executor.Set("__args", goja.Undefined()) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				// check for returned error | 
					
						
							|  |  |  | 				if res != nil { | 
					
						
							|  |  |  | 					if v, ok := res.Export().(error); ok { | 
					
						
							|  |  |  | 						return v | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				return err | 
					
						
							|  |  |  | 			}) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		return wrappedHandler, nil | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		return nil, errors.New("unsupported goja handler type") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-08 13:51:16 +03:00
										 |  |  | func wrapMiddlewares(executors *vmsPool, rawMiddlewares ...goja.Value) ([]echo.MiddlewareFunc, error) { | 
					
						
							|  |  |  | 	wrappedMiddlewares := make([]echo.MiddlewareFunc, len(rawMiddlewares)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for i, m := range rawMiddlewares { | 
					
						
							| 
									
										
										
										
											2023-07-29 16:01:38 +03:00
										 |  |  | 		if m == nil { | 
					
						
							|  |  |  | 			return nil, errors.New("middleware func must be non-nil") | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-08 13:51:16 +03:00
										 |  |  | 		switch v := m.Export().(type) { | 
					
						
							|  |  |  | 		case echo.MiddlewareFunc: | 
					
						
							|  |  |  | 			// "native" middleware - no need to wrap | 
					
						
							|  |  |  | 			wrappedMiddlewares[i] = v | 
					
						
							|  |  |  | 		case func(goja.FunctionCall) goja.Value, string: | 
					
						
							|  |  |  | 			pr := goja.MustCompile("", "{(("+m.String()+").apply(undefined, __args)).apply(undefined, __args2)}", true) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			wrappedMiddlewares[i] = func(next echo.HandlerFunc) echo.HandlerFunc { | 
					
						
							|  |  |  | 				return func(c echo.Context) error { | 
					
						
							|  |  |  | 					return executors.run(func(executor *goja.Runtime) error { | 
					
						
							|  |  |  | 						executor.Set("__args", []any{next}) | 
					
						
							|  |  |  | 						executor.Set("__args2", []any{c}) | 
					
						
							| 
									
										
										
										
											2023-07-18 12:33:18 +03:00
										 |  |  | 						res, err := executor.RunProgram(pr) | 
					
						
							| 
									
										
										
										
											2023-07-08 13:51:16 +03:00
										 |  |  | 						executor.Set("__args", goja.Undefined()) | 
					
						
							|  |  |  | 						executor.Set("__args2", goja.Undefined()) | 
					
						
							| 
									
										
										
										
											2023-07-18 12:33:18 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 						// check for returned error | 
					
						
							|  |  |  | 						if res != nil { | 
					
						
							|  |  |  | 							if v, ok := res.Export().(error); ok { | 
					
						
							|  |  |  | 								return v | 
					
						
							|  |  |  | 							} | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-08 13:51:16 +03:00
										 |  |  | 						return err | 
					
						
							|  |  |  | 					}) | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		default: | 
					
						
							|  |  |  | 			return nil, errors.New("unsupported goja middleware type") | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return wrappedMiddlewares, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-08 17:59:08 +03:00
										 |  |  | func baseBinds(vm *goja.Runtime) { | 
					
						
							| 
									
										
										
										
											2022-12-05 13:57:09 +02:00
										 |  |  | 	vm.SetFieldNameMapper(FieldMapper{}) | 
					
						
							| 
									
										
										
										
											2022-11-26 09:05:52 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-10 10:46:19 +03:00
										 |  |  | 	vm.Set("readerToString", func(r io.Reader, maxBytes int) (string, error) { | 
					
						
							|  |  |  | 		if maxBytes == 0 { | 
					
						
							|  |  |  | 			maxBytes = rest.DefaultMaxMemory | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		limitReader := io.LimitReader(r, int64(maxBytes)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		bodyBytes, readErr := io.ReadAll(limitReader) | 
					
						
							|  |  |  | 		if readErr != nil { | 
					
						
							|  |  |  | 			return "", readErr | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		return string(bodyBytes), nil | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-08 13:51:16 +03:00
										 |  |  | 	vm.Set("arrayOf", func(model any) any { | 
					
						
							| 
									
										
										
										
											2023-06-25 20:19:01 +03:00
										 |  |  | 		mt := reflect.TypeOf(model) | 
					
						
							|  |  |  | 		st := reflect.SliceOf(mt) | 
					
						
							|  |  |  | 		elem := reflect.New(st).Elem() | 
					
						
							| 
									
										
										
										
											2023-06-21 11:21:40 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-25 20:19:01 +03:00
										 |  |  | 		return elem.Addr().Interface() | 
					
						
							| 
									
										
										
										
											2023-06-21 11:21:40 +03:00
										 |  |  | 	}) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-25 20:19:01 +03:00
										 |  |  | 	vm.Set("DynamicModel", func(call goja.ConstructorCall) *goja.Object { | 
					
						
							| 
									
										
										
										
											2023-06-21 11:21:40 +03:00
										 |  |  | 		shape, ok := call.Argument(0).Export().(map[string]any) | 
					
						
							|  |  |  | 		if !ok || len(shape) == 0 { | 
					
						
							| 
									
										
										
										
											2023-07-29 16:01:38 +03:00
										 |  |  | 			panic("[DynamicModel] missing shape data") | 
					
						
							| 
									
										
										
										
											2023-06-21 11:21:40 +03:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-25 20:19:01 +03:00
										 |  |  | 		instance := newDynamicModel(shape) | 
					
						
							| 
									
										
										
										
											2023-06-21 11:21:40 +03:00
										 |  |  | 		instanceValue := vm.ToValue(instance).(*goja.Object) | 
					
						
							|  |  |  | 		instanceValue.SetPrototype(call.This.Prototype()) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		return instanceValue | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-12-05 13:57:09 +02:00
										 |  |  | 	vm.Set("Record", func(call goja.ConstructorCall) *goja.Object { | 
					
						
							|  |  |  | 		var instance *models.Record | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		collection, ok := call.Argument(0).Export().(*models.Collection) | 
					
						
							|  |  |  | 		if ok { | 
					
						
							|  |  |  | 			instance = models.NewRecord(collection) | 
					
						
							|  |  |  | 			data, ok := call.Argument(1).Export().(map[string]any) | 
					
						
							|  |  |  | 			if ok { | 
					
						
							| 
									
										
										
										
											2023-06-21 20:36:57 +03:00
										 |  |  | 				instance.Load(data) | 
					
						
							| 
									
										
										
										
											2022-12-05 13:57:09 +02:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			instance = &models.Record{} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-26 09:05:52 +02:00
										 |  |  | 		instanceValue := vm.ToValue(instance).(*goja.Object) | 
					
						
							|  |  |  | 		instanceValue.SetPrototype(call.This.Prototype()) | 
					
						
							| 
									
										
										
										
											2022-12-05 13:57:09 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-26 09:05:52 +02:00
										 |  |  | 		return instanceValue | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-12-05 13:57:09 +02:00
										 |  |  | 	vm.Set("Collection", func(call goja.ConstructorCall) *goja.Object { | 
					
						
							|  |  |  | 		instance := &models.Collection{} | 
					
						
							| 
									
										
										
										
											2023-06-21 20:36:57 +03:00
										 |  |  | 		return structConstructorUnmarshal(vm, call, instance) | 
					
						
							| 
									
										
										
										
											2022-11-26 09:05:52 +02:00
										 |  |  | 	}) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	vm.Set("Admin", func(call goja.ConstructorCall) *goja.Object { | 
					
						
							|  |  |  | 		instance := &models.Admin{} | 
					
						
							| 
									
										
										
										
											2023-06-21 20:36:57 +03:00
										 |  |  | 		return structConstructorUnmarshal(vm, call, instance) | 
					
						
							| 
									
										
										
										
											2022-11-26 09:05:52 +02:00
										 |  |  | 	}) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-27 23:00:58 +02:00
										 |  |  | 	vm.Set("Schema", func(call goja.ConstructorCall) *goja.Object { | 
					
						
							|  |  |  | 		instance := &schema.Schema{} | 
					
						
							| 
									
										
										
										
											2023-06-21 20:36:57 +03:00
										 |  |  | 		return structConstructorUnmarshal(vm, call, instance) | 
					
						
							| 
									
										
										
										
											2022-11-27 23:00:58 +02:00
										 |  |  | 	}) | 
					
						
							| 
									
										
										
										
											2022-11-28 21:56:30 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-27 23:00:58 +02:00
										 |  |  | 	vm.Set("SchemaField", func(call goja.ConstructorCall) *goja.Object { | 
					
						
							|  |  |  | 		instance := &schema.SchemaField{} | 
					
						
							| 
									
										
										
										
											2023-06-21 20:36:57 +03:00
										 |  |  | 		return structConstructorUnmarshal(vm, call, instance) | 
					
						
							| 
									
										
										
										
											2022-11-27 23:00:58 +02:00
										 |  |  | 	}) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-21 20:36:57 +03:00
										 |  |  | 	vm.Set("MailerMessage", func(call goja.ConstructorCall) *goja.Object { | 
					
						
							| 
									
										
										
										
											2023-06-08 17:59:08 +03:00
										 |  |  | 		instance := &mailer.Message{} | 
					
						
							|  |  |  | 		return structConstructor(vm, call, instance) | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-21 20:36:57 +03:00
										 |  |  | 	vm.Set("Command", func(call goja.ConstructorCall) *goja.Object { | 
					
						
							|  |  |  | 		instance := &cobra.Command{} | 
					
						
							|  |  |  | 		return structConstructor(vm, call, instance) | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-17 23:13:39 +03:00
										 |  |  | 	vm.Set("RequestInfo", func(call goja.ConstructorCall) *goja.Object { | 
					
						
							|  |  |  | 		instance := &models.RequestInfo{} | 
					
						
							|  |  |  | 		return structConstructor(vm, call, instance) | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	vm.Set("DateTime", func(call goja.ConstructorCall) *goja.Object { | 
					
						
							|  |  |  | 		instance := types.NowDateTime() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		val, _ := call.Argument(0).Export().(string) | 
					
						
							|  |  |  | 		if val != "" { | 
					
						
							|  |  |  | 			instance, _ = types.ParseDateTime(val) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		instanceValue := vm.ToValue(instance).(*goja.Object) | 
					
						
							|  |  |  | 		instanceValue.SetPrototype(call.This.Prototype()) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		return structConstructor(vm, call, instance) | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-08 17:59:08 +03:00
										 |  |  | 	vm.Set("ValidationError", func(call goja.ConstructorCall) *goja.Object { | 
					
						
							|  |  |  | 		code, _ := call.Argument(0).Export().(string) | 
					
						
							|  |  |  | 		message, _ := call.Argument(1).Export().(string) | 
					
						
							| 
									
										
										
										
											2022-11-26 09:05:52 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-08 17:59:08 +03:00
										 |  |  | 		instance := validation.NewError(code, message) | 
					
						
							| 
									
										
										
										
											2022-11-26 09:05:52 +02:00
										 |  |  | 		instanceValue := vm.ToValue(instance).(*goja.Object) | 
					
						
							|  |  |  | 		instanceValue.SetPrototype(call.This.Prototype()) | 
					
						
							| 
									
										
										
										
											2022-12-05 13:57:09 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-26 09:05:52 +02:00
										 |  |  | 		return instanceValue | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-08 17:59:08 +03:00
										 |  |  | 	vm.Set("Dao", func(call goja.ConstructorCall) *goja.Object { | 
					
						
							|  |  |  | 		concurrentDB, _ := call.Argument(0).Export().(dbx.Builder) | 
					
						
							|  |  |  | 		if concurrentDB == nil { | 
					
						
							| 
									
										
										
										
											2023-07-29 16:01:38 +03:00
										 |  |  | 			panic("[Dao] missing required Dao(concurrentDB, [nonconcurrentDB]) argument") | 
					
						
							| 
									
										
										
										
											2022-12-05 13:57:09 +02:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-08 17:59:08 +03:00
										 |  |  | 		nonConcurrentDB, _ := call.Argument(1).Export().(dbx.Builder) | 
					
						
							|  |  |  | 		if nonConcurrentDB == nil { | 
					
						
							|  |  |  | 			nonConcurrentDB = concurrentDB | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2022-12-05 13:57:09 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-08 17:59:08 +03:00
										 |  |  | 		instance := daos.NewMultiDB(concurrentDB, nonConcurrentDB) | 
					
						
							|  |  |  | 		instanceValue := vm.ToValue(instance).(*goja.Object) | 
					
						
							|  |  |  | 		instanceValue.SetPrototype(call.This.Prototype()) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		return instanceValue | 
					
						
							|  |  |  | 	}) | 
					
						
							| 
									
										
										
										
											2023-10-07 15:35:20 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	vm.Set("Cookie", func(call goja.ConstructorCall) *goja.Object { | 
					
						
							|  |  |  | 		instance := &http.Cookie{} | 
					
						
							|  |  |  | 		return structConstructor(vm, call, instance) | 
					
						
							|  |  |  | 	}) | 
					
						
							| 
									
										
										
										
											2023-10-07 16:11:31 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	vm.Set("SubscriptionMessage", func(call goja.ConstructorCall) *goja.Object { | 
					
						
							|  |  |  | 		instance := &subscriptions.Message{} | 
					
						
							|  |  |  | 		return structConstructor(vm, call, instance) | 
					
						
							|  |  |  | 	}) | 
					
						
							| 
									
										
										
										
											2022-12-05 13:57:09 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func dbxBinds(vm *goja.Runtime) { | 
					
						
							| 
									
										
										
										
											2022-11-26 09:05:52 +02:00
										 |  |  | 	obj := vm.NewObject() | 
					
						
							|  |  |  | 	vm.Set("$dbx", obj) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	obj.Set("exp", dbx.NewExp) | 
					
						
							|  |  |  | 	obj.Set("hashExp", func(data map[string]any) dbx.HashExp { | 
					
						
							| 
									
										
										
										
											2023-06-08 17:59:08 +03:00
										 |  |  | 		return dbx.HashExp(data) | 
					
						
							| 
									
										
										
										
											2022-11-26 09:05:52 +02:00
										 |  |  | 	}) | 
					
						
							|  |  |  | 	obj.Set("not", dbx.Not) | 
					
						
							|  |  |  | 	obj.Set("and", dbx.And) | 
					
						
							|  |  |  | 	obj.Set("or", dbx.Or) | 
					
						
							|  |  |  | 	obj.Set("in", dbx.In) | 
					
						
							|  |  |  | 	obj.Set("notIn", dbx.NotIn) | 
					
						
							|  |  |  | 	obj.Set("like", dbx.Like) | 
					
						
							|  |  |  | 	obj.Set("orLike", dbx.OrLike) | 
					
						
							|  |  |  | 	obj.Set("notLike", dbx.NotLike) | 
					
						
							|  |  |  | 	obj.Set("orNotLike", dbx.OrNotLike) | 
					
						
							|  |  |  | 	obj.Set("exists", dbx.Exists) | 
					
						
							|  |  |  | 	obj.Set("notExists", dbx.NotExists) | 
					
						
							|  |  |  | 	obj.Set("between", dbx.Between) | 
					
						
							|  |  |  | 	obj.Set("notBetween", dbx.NotBetween) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-17 20:50:00 +03:00
										 |  |  | func mailsBinds(vm *goja.Runtime) { | 
					
						
							|  |  |  | 	obj := vm.NewObject() | 
					
						
							|  |  |  | 	vm.Set("$mails", obj) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// admin | 
					
						
							|  |  |  | 	obj.Set("sendAdminPasswordReset", mails.SendAdminPasswordReset) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// record | 
					
						
							|  |  |  | 	obj.Set("sendRecordPasswordReset", mails.SendRecordPasswordReset) | 
					
						
							|  |  |  | 	obj.Set("sendRecordVerification", mails.SendRecordVerification) | 
					
						
							|  |  |  | 	obj.Set("sendRecordChangeEmail", mails.SendRecordChangeEmail) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-08 17:59:08 +03:00
										 |  |  | func tokensBinds(vm *goja.Runtime) { | 
					
						
							|  |  |  | 	obj := vm.NewObject() | 
					
						
							|  |  |  | 	vm.Set("$tokens", obj) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// admin | 
					
						
							|  |  |  | 	obj.Set("adminAuthToken", tokens.NewAdminAuthToken) | 
					
						
							|  |  |  | 	obj.Set("adminResetPasswordToken", tokens.NewAdminResetPasswordToken) | 
					
						
							|  |  |  | 	obj.Set("adminFileToken", tokens.NewAdminFileToken) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// record | 
					
						
							|  |  |  | 	obj.Set("recordAuthToken", tokens.NewRecordAuthToken) | 
					
						
							|  |  |  | 	obj.Set("recordVerifyToken", tokens.NewRecordVerifyToken) | 
					
						
							|  |  |  | 	obj.Set("recordResetPasswordToken", tokens.NewRecordResetPasswordToken) | 
					
						
							|  |  |  | 	obj.Set("recordChangeEmailToken", tokens.NewRecordChangeEmailToken) | 
					
						
							|  |  |  | 	obj.Set("recordFileToken", tokens.NewRecordFileToken) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func securityBinds(vm *goja.Runtime) { | 
					
						
							|  |  |  | 	obj := vm.NewObject() | 
					
						
							|  |  |  | 	vm.Set("$security", obj) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-24 11:25:00 +03:00
										 |  |  | 	// crypto | 
					
						
							|  |  |  | 	obj.Set("md5", security.MD5) | 
					
						
							|  |  |  | 	obj.Set("sha256", security.SHA256) | 
					
						
							|  |  |  | 	obj.Set("sha512", security.SHA512) | 
					
						
							| 
									
										
										
										
											2023-09-09 12:03:34 +03:00
										 |  |  | 	obj.Set("hs256", security.HS256) | 
					
						
							|  |  |  | 	obj.Set("hs512", security.HS512) | 
					
						
							|  |  |  | 	obj.Set("equal", security.Equal) | 
					
						
							| 
									
										
										
										
											2023-08-24 11:25:00 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-08 17:59:08 +03:00
										 |  |  | 	// random | 
					
						
							|  |  |  | 	obj.Set("randomString", security.RandomString) | 
					
						
							|  |  |  | 	obj.Set("randomStringWithAlphabet", security.RandomStringWithAlphabet) | 
					
						
							|  |  |  | 	obj.Set("pseudorandomString", security.PseudorandomString) | 
					
						
							|  |  |  | 	obj.Set("pseudorandomStringWithAlphabet", security.PseudorandomStringWithAlphabet) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// jwt | 
					
						
							| 
									
										
										
										
											2023-12-04 20:46:33 +02:00
										 |  |  | 	obj.Set("parseUnverifiedJWT", func(token string) (map[string]any, error) { | 
					
						
							|  |  |  | 		return security.ParseUnverifiedJWT(token) | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | 	obj.Set("parseJWT", func(token string, verificationKey string) (map[string]any, error) { | 
					
						
							|  |  |  | 		return security.ParseJWT(token, verificationKey) | 
					
						
							|  |  |  | 	}) | 
					
						
							| 
									
										
										
										
											2023-06-28 22:54:13 +03:00
										 |  |  | 	obj.Set("createJWT", security.NewJWT) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// encryption | 
					
						
							|  |  |  | 	obj.Set("encrypt", security.Encrypt) | 
					
						
							|  |  |  | 	obj.Set("decrypt", func(cipherText, key string) (string, error) { | 
					
						
							|  |  |  | 		result, err := security.Decrypt(cipherText, key) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return "", err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		return string(result), err | 
					
						
							|  |  |  | 	}) | 
					
						
							| 
									
										
										
										
											2023-06-08 17:59:08 +03:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func filesystemBinds(vm *goja.Runtime) { | 
					
						
							| 
									
										
										
										
											2022-11-26 09:05:52 +02:00
										 |  |  | 	obj := vm.NewObject() | 
					
						
							| 
									
										
										
										
											2023-06-08 17:59:08 +03:00
										 |  |  | 	vm.Set("$filesystem", obj) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	obj.Set("fileFromPath", filesystem.NewFileFromPath) | 
					
						
							|  |  |  | 	obj.Set("fileFromBytes", filesystem.NewFileFromBytes) | 
					
						
							|  |  |  | 	obj.Set("fileFromMultipart", filesystem.NewFileFromMultipart) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-24 16:39:11 +03:00
										 |  |  | func filepathBinds(vm *goja.Runtime) { | 
					
						
							|  |  |  | 	obj := vm.NewObject() | 
					
						
							|  |  |  | 	vm.Set("$filepath", obj) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	obj.Set("base", filepath.Base) | 
					
						
							|  |  |  | 	obj.Set("clean", filepath.Clean) | 
					
						
							|  |  |  | 	obj.Set("dir", filepath.Dir) | 
					
						
							|  |  |  | 	obj.Set("ext", filepath.Ext) | 
					
						
							|  |  |  | 	obj.Set("fromSlash", filepath.FromSlash) | 
					
						
							|  |  |  | 	obj.Set("glob", filepath.Glob) | 
					
						
							|  |  |  | 	obj.Set("isAbs", filepath.IsAbs) | 
					
						
							|  |  |  | 	obj.Set("join", filepath.Join) | 
					
						
							|  |  |  | 	obj.Set("match", filepath.Match) | 
					
						
							|  |  |  | 	obj.Set("rel", filepath.Rel) | 
					
						
							|  |  |  | 	obj.Set("split", filepath.Split) | 
					
						
							|  |  |  | 	obj.Set("splitList", filepath.SplitList) | 
					
						
							|  |  |  | 	obj.Set("toSlash", filepath.ToSlash) | 
					
						
							|  |  |  | 	obj.Set("walk", filepath.Walk) | 
					
						
							|  |  |  | 	obj.Set("walkDir", filepath.WalkDir) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-24 13:59:13 +03:00
										 |  |  | func osBinds(vm *goja.Runtime) { | 
					
						
							|  |  |  | 	obj := vm.NewObject() | 
					
						
							|  |  |  | 	vm.Set("$os", obj) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-24 16:39:11 +03:00
										 |  |  | 	obj.Set("args", os.Args) | 
					
						
							| 
									
										
										
										
											2023-10-14 20:08:21 +03:00
										 |  |  | 	obj.Set("exec", exec.Command) // @deprecated | 
					
						
							|  |  |  | 	obj.Set("cmd", exec.Command) | 
					
						
							| 
									
										
										
										
											2023-07-24 13:59:13 +03:00
										 |  |  | 	obj.Set("exit", os.Exit) | 
					
						
							|  |  |  | 	obj.Set("getenv", os.Getenv) | 
					
						
							|  |  |  | 	obj.Set("dirFS", os.DirFS) | 
					
						
							|  |  |  | 	obj.Set("readFile", os.ReadFile) | 
					
						
							|  |  |  | 	obj.Set("writeFile", os.WriteFile) | 
					
						
							|  |  |  | 	obj.Set("readDir", os.ReadDir) | 
					
						
							|  |  |  | 	obj.Set("tempDir", os.TempDir) | 
					
						
							|  |  |  | 	obj.Set("truncate", os.Truncate) | 
					
						
							|  |  |  | 	obj.Set("getwd", os.Getwd) | 
					
						
							|  |  |  | 	obj.Set("mkdir", os.Mkdir) | 
					
						
							|  |  |  | 	obj.Set("mkdirAll", os.MkdirAll) | 
					
						
							|  |  |  | 	obj.Set("rename", os.Rename) | 
					
						
							|  |  |  | 	obj.Set("remove", os.Remove) | 
					
						
							|  |  |  | 	obj.Set("removeAll", os.RemoveAll) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-08 17:59:08 +03:00
										 |  |  | func formsBinds(vm *goja.Runtime) { | 
					
						
							|  |  |  | 	registerFactoryAsConstructor(vm, "AdminLoginForm", forms.NewAdminLogin) | 
					
						
							|  |  |  | 	registerFactoryAsConstructor(vm, "AdminPasswordResetConfirmForm", forms.NewAdminPasswordResetConfirm) | 
					
						
							|  |  |  | 	registerFactoryAsConstructor(vm, "AdminPasswordResetRequestForm", forms.NewAdminPasswordResetRequest) | 
					
						
							|  |  |  | 	registerFactoryAsConstructor(vm, "AdminUpsertForm", forms.NewAdminUpsert) | 
					
						
							|  |  |  | 	registerFactoryAsConstructor(vm, "AppleClientSecretCreateForm", forms.NewAppleClientSecretCreate) | 
					
						
							|  |  |  | 	registerFactoryAsConstructor(vm, "CollectionUpsertForm", forms.NewCollectionUpsert) | 
					
						
							|  |  |  | 	registerFactoryAsConstructor(vm, "CollectionsImportForm", forms.NewCollectionsImport) | 
					
						
							|  |  |  | 	registerFactoryAsConstructor(vm, "RealtimeSubscribeForm", forms.NewRealtimeSubscribe) | 
					
						
							|  |  |  | 	registerFactoryAsConstructor(vm, "RecordEmailChangeConfirmForm", forms.NewRecordEmailChangeConfirm) | 
					
						
							|  |  |  | 	registerFactoryAsConstructor(vm, "RecordEmailChangeRequestForm", forms.NewRecordEmailChangeRequest) | 
					
						
							|  |  |  | 	registerFactoryAsConstructor(vm, "RecordOAuth2LoginForm", forms.NewRecordOAuth2Login) | 
					
						
							|  |  |  | 	registerFactoryAsConstructor(vm, "RecordPasswordLoginForm", forms.NewRecordPasswordLogin) | 
					
						
							|  |  |  | 	registerFactoryAsConstructor(vm, "RecordPasswordResetConfirmForm", forms.NewRecordPasswordResetConfirm) | 
					
						
							|  |  |  | 	registerFactoryAsConstructor(vm, "RecordPasswordResetRequestForm", forms.NewRecordPasswordResetRequest) | 
					
						
							|  |  |  | 	registerFactoryAsConstructor(vm, "RecordUpsertForm", forms.NewRecordUpsert) | 
					
						
							|  |  |  | 	registerFactoryAsConstructor(vm, "RecordVerificationConfirmForm", forms.NewRecordVerificationConfirm) | 
					
						
							|  |  |  | 	registerFactoryAsConstructor(vm, "RecordVerificationRequestForm", forms.NewRecordVerificationRequest) | 
					
						
							|  |  |  | 	registerFactoryAsConstructor(vm, "SettingsUpsertForm", forms.NewSettingsUpsert) | 
					
						
							|  |  |  | 	registerFactoryAsConstructor(vm, "TestEmailSendForm", forms.NewTestEmailSend) | 
					
						
							|  |  |  | 	registerFactoryAsConstructor(vm, "TestS3FilesystemForm", forms.NewTestS3Filesystem) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func apisBinds(vm *goja.Runtime) { | 
					
						
							|  |  |  | 	obj := vm.NewObject() | 
					
						
							| 
									
										
										
										
											2022-11-26 09:05:52 +02:00
										 |  |  | 	vm.Set("$apis", obj) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-24 21:11:55 +03:00
										 |  |  | 	obj.Set("staticDirectoryHandler", func(dir string, indexFallback bool) echo.HandlerFunc { | 
					
						
							|  |  |  | 		return apis.StaticDirectoryHandler(os.DirFS(dir), indexFallback) | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-26 09:05:52 +02:00
										 |  |  | 	// middlewares | 
					
						
							|  |  |  | 	obj.Set("requireRecordAuth", apis.RequireRecordAuth) | 
					
						
							|  |  |  | 	obj.Set("requireAdminAuth", apis.RequireAdminAuth) | 
					
						
							|  |  |  | 	obj.Set("requireAdminAuthOnlyIfAny", apis.RequireAdminAuthOnlyIfAny) | 
					
						
							|  |  |  | 	obj.Set("requireAdminOrRecordAuth", apis.RequireAdminOrRecordAuth) | 
					
						
							|  |  |  | 	obj.Set("requireAdminOrOwnerAuth", apis.RequireAdminOrOwnerAuth) | 
					
						
							|  |  |  | 	obj.Set("activityLogger", apis.ActivityLogger) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-08 17:59:08 +03:00
										 |  |  | 	// record helpers | 
					
						
							| 
									
										
										
										
											2023-07-17 23:13:39 +03:00
										 |  |  | 	obj.Set("requestInfo", apis.RequestInfo) | 
					
						
							| 
									
										
										
										
											2023-06-08 17:59:08 +03:00
										 |  |  | 	obj.Set("recordAuthResponse", apis.RecordAuthResponse) | 
					
						
							|  |  |  | 	obj.Set("enrichRecord", apis.EnrichRecord) | 
					
						
							|  |  |  | 	obj.Set("enrichRecords", apis.EnrichRecords) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-26 09:05:52 +02:00
										 |  |  | 	// api errors | 
					
						
							| 
									
										
										
										
											2023-06-23 22:20:13 +03:00
										 |  |  | 	registerFactoryAsConstructor(vm, "ApiError", apis.NewApiError) | 
					
						
							|  |  |  | 	registerFactoryAsConstructor(vm, "NotFoundError", apis.NewNotFoundError) | 
					
						
							|  |  |  | 	registerFactoryAsConstructor(vm, "BadRequestError", apis.NewBadRequestError) | 
					
						
							|  |  |  | 	registerFactoryAsConstructor(vm, "ForbiddenError", apis.NewForbiddenError) | 
					
						
							|  |  |  | 	registerFactoryAsConstructor(vm, "UnauthorizedError", apis.NewUnauthorizedError) | 
					
						
							| 
									
										
										
										
											2022-11-26 09:05:52 +02:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2022-11-30 17:23:00 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-11 18:09:55 +03:00
										 |  |  | func httpClientBinds(vm *goja.Runtime) { | 
					
						
							|  |  |  | 	obj := vm.NewObject() | 
					
						
							|  |  |  | 	vm.Set("$http", obj) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	type sendResult struct { | 
					
						
							| 
									
										
										
										
											2023-09-14 14:45:05 +03:00
										 |  |  | 		StatusCode int                     `json:"statusCode"` | 
					
						
							|  |  |  | 		Headers    map[string][]string     `json:"headers"` | 
					
						
							|  |  |  | 		Cookies    map[string]*http.Cookie `json:"cookies"` | 
					
						
							|  |  |  | 		Raw        string                  `json:"raw"` | 
					
						
							|  |  |  | 		Json       any                     `json:"json"` | 
					
						
							| 
									
										
										
										
											2023-07-11 18:09:55 +03:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	type sendConfig struct { | 
					
						
							|  |  |  | 		Method  string | 
					
						
							|  |  |  | 		Url     string | 
					
						
							| 
									
										
										
										
											2023-08-03 12:31:50 +03:00
										 |  |  | 		Body    string | 
					
						
							| 
									
										
										
										
											2023-07-11 18:09:55 +03:00
										 |  |  | 		Headers map[string]string | 
					
						
							| 
									
										
										
										
											2023-08-03 12:31:50 +03:00
										 |  |  | 		Timeout int            // seconds (default to 120) | 
					
						
							|  |  |  | 		Data    map[string]any // deprecated, consider using Body instead | 
					
						
							| 
									
										
										
										
											2023-07-11 18:09:55 +03:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	obj.Set("send", func(params map[string]any) (*sendResult, error) { | 
					
						
							|  |  |  | 		rawParams, err := json.Marshal(params) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return nil, err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		config := sendConfig{ | 
					
						
							|  |  |  | 			Method: "GET", | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if err := json.Unmarshal(rawParams, &config); err != nil { | 
					
						
							|  |  |  | 			return nil, err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if config.Timeout <= 0 { | 
					
						
							|  |  |  | 			config.Timeout = 120 | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		ctx, cancel := context.WithTimeout(context.Background(), time.Duration(config.Timeout)*time.Second) | 
					
						
							|  |  |  | 		defer cancel() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		var reqBody io.Reader | 
					
						
							| 
									
										
										
										
											2023-08-03 12:31:50 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		// legacy json body data | 
					
						
							| 
									
										
										
										
											2023-07-11 18:09:55 +03:00
										 |  |  | 		if len(config.Data) != 0 { | 
					
						
							|  |  |  | 			encoded, err := json.Marshal(config.Data) | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				return nil, err | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			reqBody = bytes.NewReader(encoded) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-03 12:31:50 +03:00
										 |  |  | 		if config.Body != "" { | 
					
						
							|  |  |  | 			reqBody = strings.NewReader(config.Body) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-11 18:09:55 +03:00
										 |  |  | 		req, err := http.NewRequestWithContext(ctx, strings.ToUpper(config.Method), config.Url, reqBody) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return nil, err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		for k, v := range config.Headers { | 
					
						
							|  |  |  | 			req.Header.Add(k, v) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// set default content-type header (if missing) | 
					
						
							|  |  |  | 		if req.Header.Get("content-type") == "" { | 
					
						
							|  |  |  | 			req.Header.Set("content-type", "application/json") | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		res, err := http.DefaultClient.Do(req) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return nil, err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		defer res.Body.Close() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		bodyRaw, _ := io.ReadAll(res.Body) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		result := &sendResult{ | 
					
						
							|  |  |  | 			StatusCode: res.StatusCode, | 
					
						
							| 
									
										
										
										
											2023-09-14 14:45:05 +03:00
										 |  |  | 			Headers:    map[string][]string{}, | 
					
						
							|  |  |  | 			Cookies:    map[string]*http.Cookie{}, | 
					
						
							| 
									
										
										
										
											2023-07-11 18:09:55 +03:00
										 |  |  | 			Raw:        string(bodyRaw), | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-14 14:45:05 +03:00
										 |  |  | 		for k, v := range res.Header { | 
					
						
							|  |  |  | 			result.Headers[k] = v | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		for _, v := range res.Cookies() { | 
					
						
							|  |  |  | 			result.Cookies[v.Name] = v | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-11 18:09:55 +03:00
										 |  |  | 		if len(result.Raw) != 0 { | 
					
						
							|  |  |  | 			// try as map | 
					
						
							|  |  |  | 			result.Json = map[string]any{} | 
					
						
							|  |  |  | 			if err := json.Unmarshal(bodyRaw, &result.Json); err != nil { | 
					
						
							|  |  |  | 				// try as slice | 
					
						
							|  |  |  | 				result.Json = []any{} | 
					
						
							|  |  |  | 				if err := json.Unmarshal(bodyRaw, &result.Json); err != nil { | 
					
						
							|  |  |  | 					result.Json = nil | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		return result, nil | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-08 17:59:08 +03:00
										 |  |  | // ------------------------------------------------------------------- | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // registerFactoryAsConstructor registers the factory function as native JS constructor. | 
					
						
							| 
									
										
										
										
											2023-06-23 22:20:13 +03:00
										 |  |  | // | 
					
						
							|  |  |  | // If there is missing or nil arguments, their type zero value is used. | 
					
						
							| 
									
										
										
										
											2023-06-08 17:59:08 +03:00
										 |  |  | func registerFactoryAsConstructor(vm *goja.Runtime, constructorName string, factoryFunc any) { | 
					
						
							| 
									
										
										
										
											2023-06-23 22:20:13 +03:00
										 |  |  | 	rv := reflect.ValueOf(factoryFunc) | 
					
						
							|  |  |  | 	rt := reflect.TypeOf(factoryFunc) | 
					
						
							|  |  |  | 	totalArgs := rt.NumIn() | 
					
						
							| 
									
										
										
										
											2023-06-08 17:59:08 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-23 22:20:13 +03:00
										 |  |  | 	vm.Set(constructorName, func(call goja.ConstructorCall) *goja.Object { | 
					
						
							|  |  |  | 		args := make([]reflect.Value, totalArgs) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		for i := 0; i < totalArgs; i++ { | 
					
						
							|  |  |  | 			v := call.Argument(i).Export() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// use the arg type zero value | 
					
						
							|  |  |  | 			if v == nil { | 
					
						
							|  |  |  | 				args[i] = reflect.New(rt.In(i)).Elem() | 
					
						
							|  |  |  | 			} else if number, ok := v.(int64); ok { | 
					
						
							|  |  |  | 				// goja uses int64 for "int"-like numbers but we rarely do that and use int most of the times | 
					
						
							|  |  |  | 				// (at later stage we can use reflection on the arguments to validate the types in case this is not sufficient anymore) | 
					
						
							|  |  |  | 				args[i] = reflect.ValueOf(int(number)) | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				args[i] = reflect.ValueOf(v) | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2023-06-08 17:59:08 +03:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2022-11-30 17:23:00 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-23 22:20:13 +03:00
										 |  |  | 		result := rv.Call(args) | 
					
						
							| 
									
										
										
										
											2023-06-08 17:59:08 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		if len(result) != 1 { | 
					
						
							|  |  |  | 			panic("the factory function should return only 1 item") | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		value := vm.ToValue(result[0].Interface()).(*goja.Object) | 
					
						
							|  |  |  | 		value.SetPrototype(call.This.Prototype()) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		return value | 
					
						
							|  |  |  | 	}) | 
					
						
							| 
									
										
										
										
											2022-11-30 17:23:00 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-08 17:59:08 +03:00
										 |  |  | // structConstructor wraps the provided struct with a native JS constructor. | 
					
						
							| 
									
										
										
										
											2023-06-21 20:36:57 +03:00
										 |  |  | // | 
					
						
							|  |  |  | // If the constructor argument is a map, each entry of the map will be loaded into the wrapped goja.Object. | 
					
						
							| 
									
										
										
										
											2023-06-08 17:59:08 +03:00
										 |  |  | func structConstructor(vm *goja.Runtime, call goja.ConstructorCall, instance any) *goja.Object { | 
					
						
							| 
									
										
										
										
											2023-06-21 20:36:57 +03:00
										 |  |  | 	data, _ := call.Argument(0).Export().(map[string]any) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	instanceValue := vm.ToValue(instance).(*goja.Object) | 
					
						
							|  |  |  | 	for k, v := range data { | 
					
						
							|  |  |  | 		instanceValue.Set(k, v) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	instanceValue.SetPrototype(call.This.Prototype()) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return instanceValue | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // structConstructorUnmarshal wraps the provided struct with a native JS constructor. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // The constructor first argument will be loaded via json.Unmarshal into the instance. | 
					
						
							|  |  |  | func structConstructorUnmarshal(vm *goja.Runtime, call goja.ConstructorCall, instance any) *goja.Object { | 
					
						
							| 
									
										
										
										
											2023-06-08 17:59:08 +03:00
										 |  |  | 	if data := call.Argument(0).Export(); data != nil { | 
					
						
							|  |  |  | 		if raw, err := json.Marshal(data); err == nil { | 
					
						
							|  |  |  | 			json.Unmarshal(raw, instance) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	instanceValue := vm.ToValue(instance).(*goja.Object) | 
					
						
							|  |  |  | 	instanceValue.SetPrototype(call.This.Prototype()) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return instanceValue | 
					
						
							| 
									
										
										
										
											2022-11-30 17:23:00 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-21 11:21:40 +03:00
										 |  |  | // newDynamicModel creates a new dynamic struct with fields based | 
					
						
							|  |  |  | // on the specified "shape". | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // Example: | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | //	m := newDynamicModel(map[string]any{ | 
					
						
							|  |  |  | //		"title": "", | 
					
						
							|  |  |  | //		"total": 0, | 
					
						
							|  |  |  | //	}) | 
					
						
							|  |  |  | func newDynamicModel(shape map[string]any) any { | 
					
						
							|  |  |  | 	shapeValues := make([]reflect.Value, 0, len(shape)) | 
					
						
							|  |  |  | 	structFields := make([]reflect.StructField, 0, len(shape)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for k, v := range shape { | 
					
						
							|  |  |  | 		vt := reflect.TypeOf(v) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		switch kind := vt.Kind(); kind { | 
					
						
							|  |  |  | 		case reflect.Map: | 
					
						
							|  |  |  | 			raw, _ := json.Marshal(v) | 
					
						
							|  |  |  | 			newV := types.JsonMap{} | 
					
						
							|  |  |  | 			newV.Scan(raw) | 
					
						
							|  |  |  | 			v = newV | 
					
						
							|  |  |  | 			vt = reflect.TypeOf(v) | 
					
						
							|  |  |  | 		case reflect.Slice, reflect.Array: | 
					
						
							|  |  |  | 			raw, _ := json.Marshal(v) | 
					
						
							|  |  |  | 			newV := types.JsonArray[any]{} | 
					
						
							|  |  |  | 			newV.Scan(raw) | 
					
						
							|  |  |  | 			v = newV | 
					
						
							|  |  |  | 			vt = reflect.TypeOf(newV) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		shapeValues = append(shapeValues, reflect.ValueOf(v)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		structFields = append(structFields, reflect.StructField{ | 
					
						
							| 
									
										
										
										
											2023-06-22 21:54:39 +03:00
										 |  |  | 			Name: inflector.UcFirst(k), // ensures that the field is exportable | 
					
						
							| 
									
										
										
										
											2023-06-21 11:21:40 +03:00
										 |  |  | 			Type: vt, | 
					
						
							| 
									
										
										
										
											2023-06-22 16:32:21 +03:00
										 |  |  | 			Tag:  reflect.StructTag(`db:"` + k + `" json:"` + k + `" form:"` + k + `"`), | 
					
						
							| 
									
										
										
										
											2023-06-21 11:21:40 +03:00
										 |  |  | 		}) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	st := reflect.StructOf(structFields) | 
					
						
							|  |  |  | 	elem := reflect.New(st).Elem() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for i, v := range shapeValues { | 
					
						
							|  |  |  | 		elem.Field(i).Set(v) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return elem.Addr().Interface() | 
					
						
							|  |  |  | } |