Replace javascript module otto with goja (#4631)

* Move plugin javascript to own package with goja
* Use javascript package in scraper

Remove otto
This commit is contained in:
WithoutPants 2024-03-14 11:03:40 +11:00 committed by GitHub
parent 49cd214c9d
commit 9ceea952b6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 381 additions and 288 deletions

6
go.mod
View File

@ -13,6 +13,7 @@ require (
github.com/chromedp/chromedp v0.9.2
github.com/corona10/goimagehash v1.1.0
github.com/disintegration/imaging v1.6.2
github.com/dop251/goja v0.0.0-20231027120936-b396bb4c349d
github.com/doug-martin/goqu/v9 v9.18.0
github.com/go-chi/chi/v5 v5.0.10
github.com/go-chi/cors v1.2.1
@ -35,7 +36,6 @@ require (
github.com/natefinch/pie v0.0.0-20170715172608-9a0d72014007
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8
github.com/remeh/sizedwaitgroup v1.0.0
github.com/robertkrimen/otto v0.0.0-20200922221731-ef014fd054ac
github.com/shurcooL/graphql v0.0.0-20181231061246-d48a9a75455f
github.com/sirupsen/logrus v1.9.3
github.com/spf13/cast v1.5.1
@ -67,11 +67,14 @@ require (
github.com/chromedp/sysutil v1.0.0 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dlclark/regexp2 v1.7.0 // indirect
github.com/fsnotify/fsnotify v1.6.0 // indirect
github.com/go-sourcemap/sourcemap v2.1.3+incompatible // indirect
github.com/gobwas/httphead v0.1.0 // indirect
github.com/gobwas/pool v0.2.1 // indirect
github.com/gobwas/ws v1.3.0 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/google/pprof v0.0.0-20230207041349-798e818bf904 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/hashicorp/golang-lru v0.5.4 // indirect
@ -107,6 +110,5 @@ require (
golang.org/x/mod v0.12.0 // indirect
golang.org/x/tools v0.13.0 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/sourcemap.v1 v1.0.5 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

30
go.sum
View File

@ -122,8 +122,11 @@ github.com/chromedp/chromedp v0.9.2/go.mod h1:LkSXJKONWTCHAfQasKFUZI+mxqS4tZqhmt
github.com/chromedp/sysutil v1.0.0 h1:+ZxhTpfpZlmchB58ih/LBHX52ky7w2VhQVKQMucy3Ic=
github.com/chromedp/sysutil v1.0.0/go.mod h1:kgWmDdq8fTzXYcKIBqIYvRRTnYb9aNS9moAV0xufSww=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/logex v1.2.0/go.mod h1:9+9sk7u7pGNWYMkh0hdiL++6OeibzJccyQU4p4MedaY=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/readline v1.5.0/go.mod h1:x22KAscuvRqlLoK9CsoYsmxoXZMMFVyOl86cAH8qUic=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/chzyer/test v0.0.0-20210722231415-061457976a23/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag=
github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
@ -146,6 +149,7 @@ github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:ma
github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@ -154,7 +158,15 @@ github.com/dgryski/trifles v0.0.0-20200323201526-dd97f9abfb48 h1:fRzb/w+pyskVMQ+
github.com/dgryski/trifles v0.0.0-20200323201526-dd97f9abfb48/go.mod h1:if7Fbed8SFyPtHLHbg49SI7NAdJiC5WIA09pe59rfAA=
github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c=
github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4=
github.com/dlclark/regexp2 v1.4.1-0.20201116162257-a2a8dda75c91/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
github.com/dlclark/regexp2 v1.7.0 h1:7lJfhqlPssTb1WQx4yvTHN0uElPEv52sbaECrAQxjAo=
github.com/dlclark/regexp2 v1.7.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
github.com/dop251/goja v0.0.0-20211022113120-dc8c55024d06/go.mod h1:R9ET47fwRVRPZnOGvHxxhuZcbrMCuiqOz3Rlrh4KSnk=
github.com/dop251/goja v0.0.0-20231027120936-b396bb4c349d h1:wi6jN5LVt/ljaBG4ue79Ekzb12QfJ52L9Q98tl8SWhw=
github.com/dop251/goja v0.0.0-20231027120936-b396bb4c349d/go.mod h1:QMWlm50DNe14hD7t24KEqZuUdC9sOTy8W6XbCU1mlw4=
github.com/dop251/goja_nodejs v0.0.0-20210225215109-d91c329300e7/go.mod h1:hn7BA7c8pLvoGndExHudxTDKZ84Pyvv+90pbBjbTz0Y=
github.com/dop251/goja_nodejs v0.0.0-20211022123610-8dd9abb0616d/go.mod h1:DngW8aVqWbuLRMHItjPUyqdj+HWPvnQe8V8y1nDpIbM=
github.com/doug-martin/goqu/v9 v9.18.0 h1:/6bcuEtAe6nsSMVK/M+fOiXUNfyFF3yYtE07DBPFMYY=
github.com/doug-martin/goqu/v9 v9.18.0/go.mod h1:nf0Wc2/hV3gYK9LiyqIrzBEVGlI8qW3GuDCEobC4wBQ=
github.com/dustin/go-humanize v0.0.0-20180421182945-02af3965c54e/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
@ -193,6 +205,8 @@ github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-sourcemap/sourcemap v2.1.3+incompatible h1:W1iEw64niKVGogNgBN3ePyLFfuisuzeidWPMPWmECqU=
github.com/go-sourcemap/sourcemap v2.1.3+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg=
github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
@ -285,6 +299,8 @@ github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLe
github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20230207041349-798e818bf904 h1:4/hN5RUoecvl+RmJRE2YxKWtnnQls6rQjjW5oV7qg2U=
github.com/google/pprof v0.0.0-20230207041349-798e818bf904/go.mod h1:uglQLonpP8qtYCYyzA+8c/9qtqgA3qsXGYqCPKARAFg=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
@ -344,6 +360,7 @@ github.com/huandu/xstrings v1.0.0/go.mod h1:4qWG/gcEcfX4z/mBDHJ++3ReCw9ibxbsNJbc
github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/ianlancetaylor/demangle v0.0.0-20220319035150-800ac71e25c2/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
@ -374,10 +391,13 @@ github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80 h1:6Yzfa6GP0rIo/kULo2bwGEkFvCePZ3qHDDTC3/J9Swo=
github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80/go.mod h1:imJHygn/1yfhB7XSJJKlFZKl/J+dCPAknuiaGOshXAs=
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
@ -478,10 +498,9 @@ github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsT
github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
github.com/remeh/sizedwaitgroup v1.0.0 h1:VNGGFwNo/R5+MJBf6yrsr110p0m4/OX4S3DCy7Kyl5E=
github.com/remeh/sizedwaitgroup v1.0.0/go.mod h1:3j2R4OIe/SeS6YDhICBy22RWjJC5eNCJ1V+9+NVNYlo=
github.com/robertkrimen/otto v0.0.0-20200922221731-ef014fd054ac h1:kYPjbEN6YPYWWHI6ky1J813KzIq/8+Wg4TO4xU7A/KU=
github.com/robertkrimen/otto v0.0.0-20200922221731-ef014fd054ac/go.mod h1:xvqspoSXJTIpemEonrMDFq6XzwHYYgToXWj5eRX1OtY=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
github.com/rs/xid v1.3.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
@ -821,6 +840,7 @@ golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20211205182925-97ca703d548d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@ -848,6 +868,7 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
@ -1075,8 +1096,9 @@ google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQ
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/guregu/null.v4 v4.0.0 h1:1Wm3S1WEA2I26Kq+6vcW+w0gcDo44YKYD7YIEJNHDjg=
gopkg.in/guregu/null.v4 v4.0.0/go.mod h1:YoQhUrADuG3i9WqesrCmpNRwm1ypAgSHYqoOcTu/JrI=
@ -1084,8 +1106,6 @@ gopkg.in/ini.v1 v1.66.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.66.3/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/sourcemap.v1 v1.0.5 h1:inv58fC9f9J3TK2Y2R1NPntXEn3/wjWHkonhIUODNTI=
gopkg.in/sourcemap.v1 v1.0.5/go.mod h1:2RlvNNSMglmRrcvhfuzp4hQHwOtjxlbjX7UPY/GXb78=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

106
pkg/javascript/gql.go Normal file
View File

@ -0,0 +1,106 @@
package javascript
import (
"bytes"
"context"
"encoding/json"
"fmt"
"net/http"
"strings"
"github.com/dop251/goja"
)
type responseWriter struct {
r strings.Builder
header http.Header
statusCode int
}
func (w *responseWriter) Header() http.Header {
return w.header
}
func (w *responseWriter) WriteHeader(statusCode int) {
w.statusCode = statusCode
}
func (w *responseWriter) Write(b []byte) (int, error) {
return w.r.Write(b)
}
type GQL struct {
Context context.Context
Cookie *http.Cookie
GQLHandler http.Handler
}
func (g *GQL) gqlRequestFunc(vm *VM) func(query string, variables map[string]interface{}) (goja.Value, error) {
return func(query string, variables map[string]interface{}) (goja.Value, error) {
in := struct {
Query string `json:"query"`
Variables map[string]interface{} `json:"variables,omitempty"`
}{
Query: query,
Variables: variables,
}
var body bytes.Buffer
err := json.NewEncoder(&body).Encode(in)
if err != nil {
return nil, err
}
r, err := http.NewRequestWithContext(g.Context, "POST", "/graphql", &body)
if err != nil {
return nil, fmt.Errorf("could not make request")
}
r.Header.Set("Content-Type", "application/json")
if g.Cookie != nil {
r.AddCookie(g.Cookie)
}
w := &responseWriter{
header: make(http.Header),
}
g.GQLHandler.ServeHTTP(w, r)
if w.statusCode != http.StatusOK && w.statusCode != 0 {
vm.Throw(fmt.Errorf("graphQL query failed: %d - %s. Query: %s. Variables: %v", w.statusCode, w.r.String(), in.Query, in.Variables))
}
output := w.r.String()
// convert to JSON
var obj map[string]interface{}
if err = json.Unmarshal([]byte(output), &obj); err != nil {
vm.Throw(fmt.Errorf("could not unmarshal object %s: %s", output, err.Error()))
}
retErr, hasErr := obj["errors"]
if hasErr {
errOut, _ := json.Marshal(retErr)
vm.Throw(fmt.Errorf("graphql error: %s", string(errOut)))
}
v := vm.ToValue(obj["data"])
return v, nil
}
}
func (g *GQL) AddToVM(globalName string, vm *VM) error {
gql := vm.NewObject()
if err := gql.Set("Do", g.gqlRequestFunc(vm)); err != nil {
return fmt.Errorf("unable to set GraphQL Do function: %w", err)
}
if err := vm.Set(globalName, gql); err != nil {
return fmt.Errorf("unable to set gql: %w", err)
}
return nil
}

86
pkg/javascript/log.go Normal file
View File

@ -0,0 +1,86 @@
package javascript
import (
"encoding/json"
"fmt"
"math"
"reflect"
"github.com/dop251/goja"
"github.com/stashapp/stash/pkg/logger"
)
const pluginPrefix = "[Plugin] "
type Log struct {
Progress chan float64
}
func (l *Log) argToString(call goja.FunctionCall) string {
arg := call.Argument(0)
var o map[string]interface{}
if arg.ExportType() == reflect.TypeOf(o) {
ii := arg.Export()
o = ii.(map[string]interface{})
data, err := json.Marshal(o)
if err != nil {
logger.Warnf("Couldn't json encode object")
}
return string(data)
}
return arg.String()
}
func (l *Log) logTrace(call goja.FunctionCall) goja.Value {
logger.Trace(pluginPrefix + l.argToString(call))
return nil
}
func (l *Log) logDebug(call goja.FunctionCall) goja.Value {
logger.Debug(pluginPrefix + l.argToString(call))
return nil
}
func (l *Log) logInfo(call goja.FunctionCall) goja.Value {
logger.Info(pluginPrefix + l.argToString(call))
return nil
}
func (l *Log) logWarn(call goja.FunctionCall) goja.Value {
logger.Warn(pluginPrefix + l.argToString(call))
return nil
}
func (l *Log) logError(call goja.FunctionCall) goja.Value {
logger.Error(pluginPrefix + l.argToString(call))
return nil
}
// Progress logs the current progress value. The progress value should be
// between 0 and 1.0 inclusively, with 1 representing that the task is
// complete. Values outside of this range will be clamp to be within it.
func (l *Log) logProgress(value float64) {
value = math.Min(math.Max(0, value), 1)
l.Progress <- value
}
func (l *Log) AddToVM(globalName string, vm *VM) error {
log := vm.NewObject()
if err := SetAll(log,
ObjectValueDef{"Trace", l.logTrace},
ObjectValueDef{"Debug", l.logDebug},
ObjectValueDef{"Info", l.logInfo},
ObjectValueDef{"Warn", l.logWarn},
ObjectValueDef{"Error", l.logError},
ObjectValueDef{"Progress", l.logProgress},
); err != nil {
return err
}
if err := vm.Set(globalName, log); err != nil {
return fmt.Errorf("unable to set log: %w", err)
}
return nil
}

25
pkg/javascript/util.go Normal file
View File

@ -0,0 +1,25 @@
package javascript
import (
"fmt"
"time"
)
type Util struct{}
func (u *Util) sleepFunc(ms int64) {
time.Sleep(time.Millisecond * time.Duration(ms))
}
func (u *Util) AddToVM(globalName string, vm *VM) error {
util := vm.NewObject()
if err := util.Set("Sleep", u.sleepFunc); err != nil {
return fmt.Errorf("unable to set sleep func: %w", err)
}
if err := vm.Set(globalName, util); err != nil {
return fmt.Errorf("unable to set util: %w", err)
}
return nil
}

64
pkg/javascript/vm.go Normal file
View File

@ -0,0 +1,64 @@
package javascript
import (
"fmt"
"net/http"
"os"
"github.com/dop251/goja"
)
type VM struct {
*goja.Runtime
Progress chan float64
SessionCookie *http.Cookie
GQLHandler http.Handler
}
func NewVM() *VM {
return &VM{Runtime: goja.New()}
}
type APIAdder interface {
AddToVM(globalName string, vm *VM) error
}
type ObjectValueDef struct {
Name string
Value interface{}
}
type setter interface {
Set(name string, value interface{}) error
}
func Compile(path string) (*goja.Program, error) {
js, err := os.ReadFile(path)
if err != nil {
return nil, fmt.Errorf("failed to read file: %w", err)
}
return goja.Compile(path, string(js), true)
}
func CompileScript(name, script string) (*goja.Program, error) {
return goja.Compile(name, string(script), true)
}
func SetAll(s setter, defs ...ObjectValueDef) error {
for _, def := range defs {
if err := s.Set(def.Name, def.Value); err != nil {
return fmt.Errorf("failed to set %s: %w", def.Name, err)
}
}
return nil
}
func (v *VM) Throw(err error) {
e, newErr := v.New(v.Get("Error"), v.ToValue(err))
if newErr != nil {
panic(newErr)
}
panic(e)
}

View File

@ -77,6 +77,14 @@ func (m ArgsMap) Float(key string) float64 {
return ret
}
func (m ArgsMap) ToMap() map[string]interface{} {
ret := make(map[string]interface{})
for k, v := range m {
ret[k] = v
}
return ret
}
// PluginInput is the data structure that is sent to plugin instances when they
// are spawned.
type PluginInput struct {

View File

@ -7,9 +7,9 @@ import (
"path/filepath"
"sync"
"github.com/robertkrimen/otto"
"github.com/dop251/goja"
"github.com/stashapp/stash/pkg/javascript"
"github.com/stashapp/stash/pkg/plugin/common"
"github.com/stashapp/stash/pkg/plugin/js"
)
var errStop = errors.New("stop")
@ -27,7 +27,7 @@ type jsPluginTask struct {
started bool
waitGroup sync.WaitGroup
vm *otto.Otto
vm *javascript.VM
}
func (t *jsPluginTask) onError(err error) {
@ -37,24 +37,67 @@ func (t *jsPluginTask) onError(err error) {
}
}
func (t *jsPluginTask) makeOutput(o otto.Value) {
func (t *jsPluginTask) makeOutput(o goja.Value) {
t.result = &common.PluginOutput{}
asObj := o.Object()
asObj := o.ToObject(t.vm.Runtime)
if asObj == nil {
return
}
output, _ := asObj.Get("Output")
t.result.Output, _ = output.Export()
err, _ := asObj.Get("Error")
if !err.IsUndefined() {
t.result.Output = asObj.Get("Output")
err := asObj.Get("Error")
if !goja.IsNull(err) && !goja.IsUndefined(err) {
errStr := err.String()
t.result.Error = &errStr
}
}
func (t *jsPluginTask) initVM() error {
// converting the Args field to map[string]interface{} is required, otherwise
// it gets converted to an empty object
type pluginInput struct {
// Server details to connect to the stash server.
ServerConnection common.StashServerConnection
// Arguments to the plugin operation.
Args map[string]interface{}
}
input := pluginInput{
ServerConnection: t.input.ServerConnection,
Args: t.input.Args.ToMap(),
}
if err := t.vm.Set("input", input); err != nil {
return fmt.Errorf("error setting input: %w", err)
}
log := &javascript.Log{
Progress: t.progress,
}
if err := log.AddToVM("log", t.vm); err != nil {
return fmt.Errorf("error adding log API: %w", err)
}
util := &javascript.Util{}
if err := util.AddToVM("util", t.vm); err != nil {
return fmt.Errorf("error adding util API: %w", err)
}
gql := &javascript.GQL{
Context: context.TODO(),
Cookie: t.input.ServerConnection.SessionCookie,
GQLHandler: t.gqlHandler,
}
if err := gql.AddToVM("gql", t.vm); err != nil {
return fmt.Errorf("error adding GraphQL API: %w", err)
}
return nil
}
func (t *jsPluginTask) Start() error {
if t.started {
return errors.New("task already started")
@ -68,31 +111,17 @@ func (t *jsPluginTask) Start() error {
scriptFile := t.plugin.Exec[0]
t.vm = otto.New()
t.vm = javascript.NewVM()
pluginPath := t.plugin.getConfigPath()
script, err := t.vm.Compile(filepath.Join(pluginPath, scriptFile), nil)
script, err := javascript.Compile(filepath.Join(pluginPath, scriptFile))
if err != nil {
return err
}
if err := t.vm.Set("input", t.input); err != nil {
return fmt.Errorf("error setting input: %w", err)
if err := t.initVM(); err != nil {
return err
}
if err := js.AddLogAPI(t.vm, t.progress); err != nil {
return fmt.Errorf("error adding log API: %w", err)
}
if err := js.AddUtilAPI(t.vm); err != nil {
return fmt.Errorf("error adding util API: %w", err)
}
if err := js.AddGQLAPI(context.TODO(), t.vm, t.input.ServerConnection.SessionCookie, t.gqlHandler); err != nil {
return fmt.Errorf("error adding GraphQL API: %w", err)
}
t.vm.Interrupt = make(chan func(), 1)
t.waitGroup.Add(1)
go func() {
@ -107,7 +136,7 @@ func (t *jsPluginTask) Start() error {
}
}()
output, err := t.vm.Run(script)
output, err := t.vm.RunProgram(script)
if err != nil {
t.onError(err)
@ -124,9 +153,6 @@ func (t *jsPluginTask) Wait() {
}
func (t *jsPluginTask) Stop() error {
// TODO - need another way of doing this that doesn't require panic
t.vm.Interrupt <- func() {
panic(errStop)
}
t.vm.Interrupt(errStop)
return nil
}

View File

@ -1,119 +0,0 @@
package js
import (
"bytes"
"context"
"encoding/json"
"fmt"
"net/http"
"strings"
"github.com/robertkrimen/otto"
)
type responseWriter struct {
r strings.Builder
header http.Header
statusCode int
}
func (w *responseWriter) Header() http.Header {
return w.header
}
func (w *responseWriter) WriteHeader(statusCode int) {
w.statusCode = statusCode
}
func (w *responseWriter) Write(b []byte) (int, error) {
return w.r.Write(b)
}
func throw(vm *otto.Otto, str string) {
value, _ := vm.Call("new Error", nil, str)
panic(value)
}
func gqlRequestFunc(ctx context.Context, vm *otto.Otto, cookie *http.Cookie, gqlHandler http.Handler) func(call otto.FunctionCall) otto.Value {
return func(call otto.FunctionCall) otto.Value {
if len(call.ArgumentList) == 0 {
throw(vm, "missing argument")
}
query := call.Argument(0)
vars := call.Argument(1)
var variables map[string]interface{}
if !vars.IsUndefined() {
exported, _ := vars.Export()
variables, _ = exported.(map[string]interface{})
}
in := struct {
Query string `json:"query"`
Variables map[string]interface{} `json:"variables,omitempty"`
}{
Query: query.String(),
Variables: variables,
}
var body bytes.Buffer
err := json.NewEncoder(&body).Encode(in)
if err != nil {
throw(vm, err.Error())
}
r, err := http.NewRequestWithContext(ctx, "POST", "/graphql", &body)
if err != nil {
throw(vm, "could not make request")
}
r.Header.Set("Content-Type", "application/json")
if cookie != nil {
r.AddCookie(cookie)
}
w := &responseWriter{
header: make(http.Header),
}
gqlHandler.ServeHTTP(w, r)
if w.statusCode != http.StatusOK && w.statusCode != 0 {
throw(vm, fmt.Sprintf("graphQL query failed: %d - %s. Query: %s. Variables: %v", w.statusCode, w.r.String(), in.Query, in.Variables))
}
output := w.r.String()
// convert to JSON
var obj map[string]interface{}
if err = json.Unmarshal([]byte(output), &obj); err != nil {
throw(vm, fmt.Sprintf("could not unmarshal object %s: %s", output, err.Error()))
}
retErr, hasErr := obj["errors"]
if hasErr {
errOut, _ := json.Marshal(retErr)
throw(vm, fmt.Sprintf("graphql error: %s", string(errOut)))
}
v, err := vm.ToValue(obj["data"])
if err != nil {
throw(vm, fmt.Sprintf("could not create return value: %s", err.Error()))
}
return v
}
}
func AddGQLAPI(ctx context.Context, vm *otto.Otto, cookie *http.Cookie, gqlHandler http.Handler) error {
gql, _ := vm.Object("({})")
if err := gql.Set("Do", gqlRequestFunc(ctx, vm, cookie, gqlHandler)); err != nil {
return fmt.Errorf("unable to set GraphQL Do function: %w", err)
}
if err := vm.Set("gql", gql); err != nil {
return fmt.Errorf("unable to set gql: %w", err)
}
return nil
}

View File

@ -1,96 +0,0 @@
package js
import (
"encoding/json"
"fmt"
"math"
"github.com/robertkrimen/otto"
"github.com/stashapp/stash/pkg/logger"
)
const pluginPrefix = "[Plugin] "
func argToString(call otto.FunctionCall) string {
arg := call.Argument(0)
if arg.IsObject() {
o, _ := arg.Export()
data, err := json.Marshal(o)
if err != nil {
logger.Warnf("Couldn't json encode object")
}
return string(data)
}
return arg.String()
}
func logTrace(call otto.FunctionCall) otto.Value {
logger.Trace(pluginPrefix + argToString(call))
return otto.UndefinedValue()
}
func logDebug(call otto.FunctionCall) otto.Value {
logger.Debug(pluginPrefix + argToString(call))
return otto.UndefinedValue()
}
func logInfo(call otto.FunctionCall) otto.Value {
logger.Info(pluginPrefix + argToString(call))
return otto.UndefinedValue()
}
func logWarn(call otto.FunctionCall) otto.Value {
logger.Warn(pluginPrefix + argToString(call))
return otto.UndefinedValue()
}
func logError(call otto.FunctionCall) otto.Value {
logger.Error(pluginPrefix + argToString(call))
return otto.UndefinedValue()
}
// Progress logs the current progress value. The progress value should be
// between 0 and 1.0 inclusively, with 1 representing that the task is
// complete. Values outside of this range will be clamp to be within it.
func logProgressFunc(c chan float64) func(call otto.FunctionCall) otto.Value {
return func(call otto.FunctionCall) otto.Value {
arg := call.Argument(0)
if !arg.IsNumber() {
return otto.UndefinedValue()
}
progress, _ := arg.ToFloat()
progress = math.Min(math.Max(0, progress), 1)
c <- progress
return otto.UndefinedValue()
}
}
func AddLogAPI(vm *otto.Otto, progress chan float64) error {
log, _ := vm.Object("({})")
if err := log.Set("Trace", logTrace); err != nil {
return fmt.Errorf("error setting Trace: %w", err)
}
if err := log.Set("Debug", logDebug); err != nil {
return fmt.Errorf("error setting Debug: %w", err)
}
if err := log.Set("Info", logInfo); err != nil {
return fmt.Errorf("error setting Info: %w", err)
}
if err := log.Set("Warn", logWarn); err != nil {
return fmt.Errorf("error setting Warn: %w", err)
}
if err := log.Set("Error", logError); err != nil {
return fmt.Errorf("error setting Error: %w", err)
}
if err := log.Set("Progress", logProgressFunc(progress)); err != nil {
return fmt.Errorf("error setting Progress: %v", err)
}
if err := vm.Set("log", log); err != nil {
return fmt.Errorf("unable to set log: %w", err)
}
return nil
}

View File

@ -1,29 +0,0 @@
package js
import (
"fmt"
"time"
"github.com/robertkrimen/otto"
)
func sleepFunc(call otto.FunctionCall) otto.Value {
arg := call.Argument(0)
ms, _ := arg.ToInteger()
time.Sleep(time.Millisecond * time.Duration(ms))
return otto.UndefinedValue()
}
func AddUtilAPI(vm *otto.Otto) error {
util, _ := vm.Object("({})")
if err := util.Set("Sleep", sleepFunc); err != nil {
return fmt.Errorf("unable to set sleep func: %w", err)
}
if err := vm.Set("util", util); err != nil {
return fmt.Errorf("unable to set util: %w", err)
}
return nil
}

View File

@ -11,9 +11,9 @@ import (
"strings"
"time"
"github.com/robertkrimen/otto"
"gopkg.in/yaml.v2"
"github.com/stashapp/stash/pkg/javascript"
"github.com/stashapp/stash/pkg/logger"
"github.com/stashapp/stash/pkg/models"
"github.com/stashapp/stash/pkg/sliceutil"
@ -528,19 +528,19 @@ func (p *postProcessLbToKg) Apply(ctx context.Context, value string, q mappedQue
type postProcessJavascript string
func (p *postProcessJavascript) Apply(ctx context.Context, value string, q mappedQuery) string {
vm := otto.New()
vm := javascript.NewVM()
if err := vm.Set("value", value); err != nil {
logger.Warnf("javascript failed to set value: %v", err)
return value
}
script, err := vm.Compile("", "(function() { "+string(*p)+"})()")
script, err := javascript.CompileScript("", "(function() { "+string(*p)+"})()")
if err != nil {
logger.Warnf("javascript failed to compile: %v", err)
return value
}
output, err := vm.Run(script)
output, err := vm.RunProgram(script)
if err != nil {
logger.Warnf("javascript failed to run: %v", err)
return value