pull/3757/head
Ryan 4 months ago
parent 67e6f05a00
commit 24d64b4ca4

@ -42,6 +42,7 @@ module.exports = {
'error',
{ allowSameFolder: true },
],
"@next/next/no-document-import-in-page": "off"
},
overrides: [
{

@ -1,5 +0,0 @@
root: ./docs
structure:
readme: README.md
summary: SUMMARY.md

@ -1,4 +0,0 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
[[ -n $HUSKY_BYPASS ]] || npx commitlint --edit $1

@ -1,4 +0,0 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
npx lint-staged

@ -1,4 +0,0 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
exec < /dev/tty && npx cz --hook || true

@ -0,0 +1,41 @@
module overseerr
go 1.21
require (
github.com/buger/jsonparser v1.1.1 // indirect
github.com/bytedance/sonic v1.10.2 // indirect
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect
github.com/chenzhuoyu/iasm v0.9.1 // indirect
github.com/evanw/esbuild v0.19.11 // indirect
github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/gin-gonic/gin v1.9.1 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.17.0 // indirect
github.com/goccy/go-json v0.10.2 // indirect
github.com/gorilla/websocket v1.5.1 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/cpuid/v2 v2.2.6 // indirect
github.com/leodido/go-urn v1.2.4 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/natewong1313/go-react-ssr v0.1.15 // indirect
github.com/pelletier/go-toml/v2 v2.1.1 // indirect
github.com/rs/zerolog v1.31.0 // indirect
github.com/tkrajina/go-reflector v0.5.6 // indirect
github.com/tkrajina/typescriptify-golang-structs v0.1.11 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.2.12 // indirect
golang.org/x/arch v0.7.0 // indirect
golang.org/x/crypto v0.18.0 // indirect
golang.org/x/net v0.20.0 // indirect
golang.org/x/sys v0.16.0 // indirect
golang.org/x/text v0.14.0 // indirect
google.golang.org/protobuf v1.32.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

112
go.sum

@ -0,0 +1,112 @@
github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs=
github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0=
github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
github.com/bytedance/sonic v1.10.0-rc/go.mod h1:ElCzW+ufi8qKqNW0FY314xriJhyJhuoJ3gFZdAHF7NM=
github.com/bytedance/sonic v1.10.2 h1:GQebETVBxYB7JGWJtLBi07OVzWwt+8dWA00gEVW2ZFE=
github.com/bytedance/sonic v1.10.2/go.mod h1:iZcSUejdk5aukTND/Eu/ivjQuEL0Cu9/rf50Hi0u/g4=
github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d h1:77cEq6EriyTZ0g/qfRdp61a3Uu/AWrgIq2s0ClJV1g0=
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d/go.mod h1:8EPpVsBuRksnlj1mLy4AWzRNQYxauNi62uWcE3to6eA=
github.com/chenzhuoyu/iasm v0.9.0/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog=
github.com/chenzhuoyu/iasm v0.9.1 h1:tUHQJXo3NhBqw6s33wkGn9SP3bvrWLdlVIJ3hQBL7P0=
github.com/chenzhuoyu/iasm v0.9.1/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog=
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/evanw/esbuild v0.19.11 h1:mbPO1VJ/df//jjUd+p/nRLYCpizXxXb2w/zZMShxa2k=
github.com/evanw/esbuild v0.19.11/go.mod h1:D2vIQZqV/vIf/VRHtViaUtViZmG7o+kKmlBfVQuRi48=
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg=
github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU=
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.17.0 h1:SmVVlfAOtlZncTxRuinDPomC2DkXJ4E5T9gDA0AIH74=
github.com/go-playground/validator/v10 v10.17.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY=
github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.2.6 h1:ndNyv040zDGIDh8thGkXYjnFtiN02M1PVVF+JE/48xc=
github.com/klauspost/cpuid/v2 v2.2.6/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q=
github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/natewong1313/go-react-ssr v0.1.15 h1:vycZXFFnsZfxCaGKLJKM3oTeRzVfEtHHaHnSSHVmUHE=
github.com/natewong1313/go-react-ssr v0.1.15/go.mod h1:VRY3QQsqo6Yhrd7d26a4nXkaYeQdOKF95bsSBPj/riY=
github.com/pelletier/go-toml/v2 v2.1.1 h1:LWAJwfNvjQZCFIDKWYQaM62NcYeYViCmWIwmOStowAI=
github.com/pelletier/go-toml/v2 v2.1.1/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
github.com/rs/zerolog v1.31.0 h1:FcTR3NnLWW+NnTwwhFWiJSZr4ECLpqCm6QsEnyvbV4A=
github.com/rs/zerolog v1.31.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/tkrajina/go-reflector v0.5.5/go.mod h1:ECbqLgccecY5kPmPmXg1MrHW585yMcDkVl6IvJe64T4=
github.com/tkrajina/go-reflector v0.5.6 h1:hKQ0gyocG7vgMD2M3dRlYN6WBBOmdoOzJ6njQSepKdE=
github.com/tkrajina/go-reflector v0.5.6/go.mod h1:ECbqLgccecY5kPmPmXg1MrHW585yMcDkVl6IvJe64T4=
github.com/tkrajina/typescriptify-golang-structs v0.1.11 h1:zEIVczF/iWgs4eTY7NQqbBe23OVlFVk9sWLX/FDYi4Q=
github.com/tkrajina/typescriptify-golang-structs v0.1.11/go.mod h1:sjU00nti/PMEOZb07KljFlR+lJ+RotsC0GBQMv9EKls=
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
golang.org/x/arch v0.7.0 h1:pskyeJh/3AmoQ8CPE95vxHLqp1G1GfGNXTmcl9NEKTc=
golang.org/x/arch v0.7.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc=
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo=
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I=
google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=

@ -0,0 +1,41 @@
package main
import (
"log"
"math/rand"
"overseerr/models"
"github.com/gin-gonic/gin"
gossr "github.com/natewong1313/go-react-ssr"
)
func main() {
engine, err := gossr.New(gossr.Config{
AssetRoute: "./src/assets",
FrontendDir: "./src",
GeneratedTypesPath: "./src/generated.d.ts",
PropsStructsPath: "./models/props.go",
// TailwindConfigPath: "./tailwind.config.js",
})
if err != nil {
log.Fatal(err)
}
r := gin.Default()
r.GET("/", func(c *gin.Context) {
renderedResponse := engine.RenderRoute(gossr.RenderConfig{
File: "pages/_document.tsx",
Title: "Pages app",
MetaTags: map[string]string{
"og:title": "Example app",
"description": "Hello world!",
},
Props: &models.IndexRouteProps{
InitialCount: rand.Intn(100),
},
})
c.Writer.Write(renderedResponse)
})
r.Run() // listen and serve on 0.0.0.0:8080
}

@ -0,0 +1,5 @@
package models
type IndexRouteProps struct {
InitialCount int `json:"initialCount"`
}

28218
package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -3,25 +3,7 @@
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "nodemon -e ts --watch server --watch overseerr-api.yml -e .json,.ts,.yml -x ts-node -r tsconfig-paths/register --files --project server/tsconfig.json server/index.ts",
"build:server": "tsc --project server/tsconfig.json && copyfiles -u 2 server/templates/**/*.{html,pug} dist/templates && tsc-alias -p server/tsconfig.json",
"build:next": "next build",
"build": "yarn build:next && yarn build:server",
"lint": "eslint \"./server/**/*.{ts,tsx}\" \"./src/**/*.{ts,tsx}\" --cache",
"start": "NODE_ENV=production node dist/index.js",
"i18n:extract": "extract-messages -l=en -o src/i18n/locale -d en --flat true --overwriteDefault true \"./src/**/!(*.test).{ts,tsx}\"",
"migration:generate": "ts-node -r tsconfig-paths/register --project server/tsconfig.json ./node_modules/typeorm/cli.js migration:generate -d server/datasource.ts",
"migration:create": "ts-node -r tsconfig-paths/register --project server/tsconfig.json ./node_modules/typeorm/cli.js migration:create -d server/datasource.ts",
"migration:run": "ts-node -r tsconfig-paths/register --project server/tsconfig.json ./node_modules/typeorm/cli.js migration:run -d server/datasource.ts",
"format": "prettier --loglevel warn --write --cache .",
"format:check": "prettier --check --cache .",
"typecheck": "yarn typecheck:server && yarn typecheck:client",
"typecheck:server": "tsc --project server/tsconfig.json --noEmit",
"typecheck:client": "tsc --noEmit",
"prepare": "husky install",
"cypress:open": "cypress open",
"cypress:prepare": "ts-node -r tsconfig-paths/register --files --project server/tsconfig.json server/scripts/prepareTestDb.ts",
"cypress:build": "yarn build && yarn cypress:prepare"
"lint": "next lint"
},
"repository": {
"type": "git",
@ -60,12 +42,10 @@
"express-session": "1.17.3",
"formik": "2.2.9",
"gravatar-url": "3.1.0",
"http-proxy-agent": "^7.0.0",
"https-proxy-agent": "^7.0.2",
"intl": "1.2.5",
"lodash": "4.17.21",
"needle": "^3.3.1",
"next": "12.3.4",
"next": "^14.1.0",
"node-cache": "5.1.2",
"node-gyp": "9.3.1",
"node-schedule": "2.1.1",
@ -85,7 +65,6 @@
"react-select": "5.7.0",
"react-spring": "9.7.1",
"react-tailwindcss-datepicker-sct": "1.3.4",
"react-toast-notifications": "2.5.1",
"react-truncate-markup": "5.1.2",
"react-use-clipboard": "1.0.9",
"reflect-metadata": "0.1.13",
@ -108,10 +87,6 @@
"@babel/cli": "7.21.0",
"@commitlint/cli": "17.4.4",
"@commitlint/config-conventional": "17.4.4",
"@semantic-release/changelog": "6.0.2",
"@semantic-release/commit-analyzer": "9.0.2",
"@semantic-release/exec": "6.0.3",
"@semantic-release/git": "10.0.1",
"@tailwindcss/aspect-ratio": "0.4.2",
"@tailwindcss/forms": "0.5.3",
"@tailwindcss/typography": "0.5.9",
@ -147,7 +122,7 @@
"cypress": "^13.6.2",
"cz-conventional-changelog": "3.3.0",
"eslint": "8.35.0",
"eslint-config-next": "12.3.4",
"eslint-config-next": "14.1.0",
"eslint-config-prettier": "8.6.0",
"eslint-plugin-formatjs": "4.9.0",
"eslint-plugin-jsx-a11y": "6.7.1",
@ -163,87 +138,10 @@
"prettier": "2.8.4",
"prettier-plugin-organize-imports": "3.2.2",
"prettier-plugin-tailwindcss": "0.2.3",
"semantic-release": "^22.0.12",
"semantic-release-docker-buildx": "1.0.1",
"tailwindcss": "3.2.7",
"ts-node": "10.9.1",
"tsc-alias": "1.8.2",
"tsconfig-paths": "4.1.2",
"typescript": "4.9.5"
},
"resolutions": {
"sqlite3/node-gyp": "8.4.1",
"@types/react": "18.0.28",
"@types/react-dom": "18.0.11",
"@types/express-session": "1.17.6",
"next/@swc/helpers": "0.4.36"
},
"config": {
"commitizen": {
"path": "./node_modules/cz-conventional-changelog"
}
},
"lint-staged": {
"**/*.{ts,tsx,js}": [
"prettier --write",
"eslint"
],
"**/*.{json,css}": [
"prettier --write"
]
},
"commitlint": {
"extends": [
"@commitlint/config-conventional"
]
},
"release": {
"plugins": [
"@semantic-release/commit-analyzer",
"@semantic-release/release-notes-generator",
[
"@semantic-release/changelog",
{
"changelogFile": "CHANGELOG.md"
}
],
"@semantic-release/npm",
[
"@semantic-release/git",
{
"assets": [
"package.json",
"CHANGELOG.md"
],
"message": "chore(release): ${nextRelease.version}"
}
],
"semantic-release-docker-buildx",
[
"@semantic-release/github",
{
"addReleases": "bottom"
}
]
],
"branches": [
"master"
],
"npmPublish": false,
"publish": [
{
"path": "semantic-release-docker-buildx",
"buildArgs": {
"COMMIT_TAG": "$GIT_SHA"
},
"imageNames": [
"ghcr.io/ryanc1256/overseerr"
],
"platforms": [
"linux/amd64"
]
},
"@semantic-release/github"
]
}
}

@ -18,7 +18,6 @@ import { Field, Form, Formik } from 'formik';
import { useCallback, useEffect, useState } from 'react';
import { defineMessages, useIntl } from 'react-intl';
import AsyncSelect from 'react-select/async';
import { useToasts } from 'react-toast-notifications';
import * as Yup from 'yup';
const messages = defineMessages({
@ -61,7 +60,6 @@ type CreateOption = {
const CreateSlider = ({ onCreate, slider }: CreateSliderProps) => {
const intl = useIntl();
const { addToast } = useToasts();
const [resultCount, setResultCount] = useState(0);
const [defaultDataValue, setDefaultDataValue] = useState<
{ label: string; value: number }[] | null
@ -326,25 +324,25 @@ const CreateSlider = ({ onCreate, slider }: CreateSliderProps) => {
});
}
addToast(
intl.formatMessage(
slider ? messages.editsuccess : messages.addsuccess
),
{
appearance: 'success',
autoDismiss: true,
}
);
// addToast(
// intl.formatMessage(
// slider ? messages.editsuccess : messages.addsuccess
// ),
// {
// appearance: 'success',
// autoDismiss: true,
// }
// );
onCreate();
resetForm();
} catch (e) {
addToast(
intl.formatMessage(slider ? messages.editfail : messages.addfail),
{
appearance: 'error',
autoDismiss: true,
}
);
// addToast(
// intl.formatMessage(slider ? messages.editfail : messages.addfail),
// {
// appearance: 'error',
// autoDismiss: true,
// }
// );
}
}}
>

@ -23,7 +23,7 @@ import axios from 'axios';
import { useRef, useState } from 'react';
import { useDrag, useDrop } from 'react-aria';
import { defineMessages, useIntl } from 'react-intl';
import { useToasts } from 'react-toast-notifications';
// import { useToasts } from 'react-toast-notifications';
const messages = defineMessages({
deletesuccess: 'Sucessfully deleted slider.',
@ -62,7 +62,7 @@ const DiscoverSliderEdit = ({
disableDownButton,
}: DiscoverSliderEditProps) => {
const intl = useIntl();
const { addToast } = useToasts();
// const { addToast } = useToasts();
const [isEditing, setIsEditing] = useState(false);
const ref = useRef<HTMLDivElement>(null);
const [hoverPosition, setHoverPosition] = useState<keyof typeof Position>(
@ -78,16 +78,16 @@ const DiscoverSliderEdit = ({
const deleteSlider = async () => {
try {
await axios.delete(`/api/v1/settings/discover/${slider.id}`);
addToast(intl.formatMessage(messages.deletesuccess), {
appearance: 'success',
autoDismiss: true,
});
// addToast(intl.formatMessage(messages.deletesuccess), {
// appearance: 'success',
// autoDismiss: true,
// });
onDelete();
} catch (e) {
addToast(intl.formatMessage(messages.deletefail), {
appearance: 'error',
autoDismiss: true,
});
// addToast(intl.formatMessage(messages.deletefail), {
// appearance: 'error',
// autoDismiss: true,
// });
}
};

@ -30,7 +30,6 @@ import type DiscoverSlider from '@server/entity/DiscoverSlider';
import axios from 'axios';
import { useEffect, useState } from 'react';
import { defineMessages, useIntl } from 'react-intl';
import { useToasts } from 'react-toast-notifications';
import useSWR from 'swr';
const messages = defineMessages({
@ -54,7 +53,6 @@ const messages = defineMessages({
const Discover = () => {
const intl = useIntl();
const { hasPermission } = useUser();
const { addToast } = useToasts();
const {
data: discoverData,
error: discoverError,
@ -74,39 +72,39 @@ const Discover = () => {
const hasChanged = () => !Object.is(discoverData, sliders);
const updateSliders = async () => {
try {
await axios.post('/api/v1/settings/discover', sliders);
await axios.post('/api/v1/settings/discover', sliders);
// try {
addToast(intl.formatMessage(messages.updatesuccess), {
appearance: 'success',
autoDismiss: true,
});
setIsEditing(false);
mutate();
} catch (e) {
addToast(intl.formatMessage(messages.updatefailed), {
appearance: 'error',
autoDismiss: true,
});
}
// addToast(intl.formatMessage(messages.updatesuccess), {
// appearance: 'success',
// autoDismiss: true,
// });
// setIsEditing(false);
// mutate();
// } catch (e) {
// addToast(intl.formatMessage(messages.updatefailed), {
// appearance: 'error',
// autoDismiss: true,
// });
// }
};
const resetSliders = async () => {
try {
await axios.get('/api/v1/settings/discover/reset');
await axios.get('/api/v1/settings/discover/reset');
// try {
addToast(intl.formatMessage(messages.resetsuccess), {
appearance: 'success',
autoDismiss: true,
});
setIsEditing(false);
mutate();
} catch (e) {
addToast(intl.formatMessage(messages.resetfailed), {
appearance: 'error',
autoDismiss: true,
});
}
// addToast(intl.formatMessage(messages.resetsuccess), {
// appearance: 'success',
// autoDismiss: true,
// });
// setIsEditing(false);
// mutate();
// } catch (e) {
// addToast(intl.formatMessage(messages.resetfailed), {
// appearance: 'error',
// autoDismiss: true,
// });
// }
};
const now = new Date();

@ -23,7 +23,6 @@ import {
XMarkIcon,
} from '@heroicons/react/24/solid';
import Link from 'next/link';
import { useRouter } from 'next/router';
import { cloneElement, useRef, useState } from 'react';
import { useIntl } from 'react-intl';
@ -44,7 +43,6 @@ const MobileMenu = () => {
const intl = useIntl();
const [isOpen, setIsOpen] = useState(false);
const { hasPermission } = useUser();
const router = useRouter();
useClickOutside(ref, () => {
setTimeout(() => {
if (isOpen) {
@ -125,6 +123,8 @@ const MobileMenu = () => {
})
);
const path = '';
return (
<div className="fixed bottom-0 left-0 right-0 z-50">
<Transition
@ -140,7 +140,7 @@ const MobileMenu = () => {
className="absolute top-0 left-0 right-0 flex w-full -translate-y-full flex-col space-y-6 border-t border-gray-600 bg-gray-900 bg-opacity-90 px-6 py-6 font-semibold text-gray-100 backdrop-blur"
>
{filteredLinks.map((link) => {
const isActive = router.pathname.match(link.activeRegExp);
const isActive = path.match(link.activeRegExp);
return (
<Link key={`mobile-menu-link-${link.href}`} href={link.href}>
<a
@ -170,8 +170,7 @@ const MobileMenu = () => {
{filteredLinks
.slice(0, filteredLinks.length === 5 ? 5 : 4)
.map((link) => {
const isActive =
router.pathname.match(link.activeRegExp) && !isOpen;
const isActive = path.match(link.activeRegExp) && !isOpen;
return (
<Link key={`mobile-menu-link-${link.href}`} href={link.href}>
<a

@ -1,10 +1,7 @@
import { ArrowPathIcon } from '@heroicons/react/24/outline';
import { useRouter } from 'next/router';
import { useEffect, useRef, useState } from 'react';
const PullToRefresh = () => {
const router = useRouter();
const [pullStartPoint, setPullStartPoint] = useState(0);
const [pullChange, setPullChange] = useState(0);
const refreshDiv = useRef<HTMLDivElement>(null);
@ -21,7 +18,7 @@ const PullToRefresh = () => {
const forceReload = () => {
refreshDiv.current?.classList.add('loading');
setTimeout(() => {
router.reload();
location.reload();
}, 1000);
};
@ -83,7 +80,7 @@ const PullToRefresh = () => {
window.removeEventListener('touchmove', pullDown);
window.removeEventListener('touchend', pullFinish);
};
}, [pullDownInitThreshold, pullDownReloadThreshold, pullStartPoint, router]);
}, [pullDownInitThreshold, pullDownReloadThreshold, pullStartPoint]);
return (
<div

@ -13,7 +13,6 @@ import {
XMarkIcon,
} from '@heroicons/react/24/outline';
import Link from 'next/link';
import { useRouter } from 'next/router';
import { Fragment, useRef } from 'react';
import { defineMessages, useIntl } from 'react-intl';
@ -100,10 +99,10 @@ const SidebarLinks: SidebarLinkProps[] = [
const Sidebar = ({ open, setClosed }: SidebarProps) => {
const navRef = useRef<HTMLDivElement>(null);
const router = useRouter();
const intl = useIntl();
const { hasPermission } = useUser();
useClickOutside(navRef, () => setClosed());
const path = '';
return (
<>
@ -179,9 +178,7 @@ const Sidebar = ({ open, setClosed }: SidebarProps) => {
tabIndex={0}
className={`flex items-center rounded-md px-2 py-2 text-base font-medium leading-6 text-white transition duration-150 ease-in-out focus:outline-none
${
router.pathname.match(
sidebarLink.activeRegExp
)
path.match(sidebarLink.activeRegExp)
? 'bg-gradient-to-br from-indigo-600 to-purple-600 hover:from-indigo-500 hover:to-purple-500'
: 'hover:bg-gray-700 focus:bg-gray-700'
}
@ -241,9 +238,7 @@ const Sidebar = ({ open, setClosed }: SidebarProps) => {
<a
className={`group flex items-center rounded-md px-2 py-2 text-lg font-medium leading-6 text-white transition duration-150 ease-in-out focus:outline-none
${
router.pathname.match(
sidebarLink.activeRegExp
)
path.match(sidebarLink.activeRegExp)
? 'bg-gradient-to-br from-indigo-600 to-purple-600 hover:from-indigo-500 hover:to-purple-500'
: 'hover:bg-gray-700 focus:bg-gray-700'
}

@ -31,12 +31,12 @@ const VersionStatus = ({ onClick }: VersionStatusProps) => {
return null;
}
const versionStream =
data.commitTag === 'local'
? 'Keep it up! 👍'
: data.version.startsWith('develop-')
? intl.formatMessage(messages.streamdevelop)
: intl.formatMessage(messages.streamstable);
// const versionStream =
// data.commitTag === 'local'
// ? 'Keep it up! 👍'
// : data.version.startsWith('develop-')
// ? intl.formatMessage(messages.streamdevelop)
// : intl.formatMessage(messages.streamstable);
return (
<Link href="/settings/about">
@ -62,7 +62,7 @@ const VersionStatus = ({ onClick }: VersionStatusProps) => {
) : (
<ServerIcon className="h-6 w-6" />
)}
<div className="flex min-w-0 flex-1 flex-col truncate px-2 last:pr-0">
{/* <div className="flex min-w-0 flex-1 flex-col truncate px-2 last:pr-0">
<span className="font-bold">{versionStream}</span>
<span className="truncate">
{data.commitTag === 'local' ? (
@ -79,7 +79,7 @@ const VersionStatus = ({ onClick }: VersionStatusProps) => {
</code>
)}
</span>
</div>
</div> */}
{data.updateAvailable && <ArrowUpCircleIcon className="h-6 w-6" />}
</a>
</Link>

@ -8,7 +8,6 @@ import useLocale from '@app/hooks/useLocale';
import useSettings from '@app/hooks/useSettings';
import { useUser } from '@app/hooks/useUser';
import { ArrowLeftIcon, Bars3BottomLeftIcon } from '@heroicons/react/24/solid';
import { useRouter } from 'next/router';
import { useEffect, useState } from 'react';
type LayoutProps = {
@ -19,7 +18,6 @@ const Layout = ({ children }: LayoutProps) => {
const [isSidebarOpen, setSidebarOpen] = useState(false);
const [isScrolled, setIsScrolled] = useState(false);
const { user } = useUser();
const router = useRouter();
const { currentSettings } = useSettings();
const { setLocale } = useLocale();
@ -86,7 +84,7 @@ const Layout = ({ children }: LayoutProps) => {
className={`mr-2 text-white ${
isScrolled ? 'opacity-90' : 'opacity-70'
} pwa-only transition duration-300 hover:text-white focus:text-white focus:outline-none`}
onClick={() => router.back()}
onClick={() => history.back()}
>
<ArrowLeftIcon className="w-7" />
</button>

@ -1,5 +1,4 @@
import { NProgress } from '@tanem/react-nprogress';
import { useRouter } from 'next/router';
import React, { useEffect, useState } from 'react';
import ReactDOM from 'react-dom';
@ -39,29 +38,10 @@ const MemoizedNProgress = React.memo(NProgressBar);
const LoadingBar = (): React.ReactPortal | null => {
const [mounted, setMounted] = useState(false);
const [loading, setLoading] = useState(false);
const router = useRouter();
useEffect(() => {
setMounted(true);
}, []);
useEffect(() => {
const handleLoading = () => {
setLoading(true);
};
const handleFinishedLoading = () => {
setLoading(false);
};
router.events.on('routeChangeStart', handleLoading);
router.events.on('routeChangeComplete', handleFinishedLoading);
router.events.on('routeChangeError', handleFinishedLoading);
return () => {
router.events.off('routeChangeStart', handleLoading);
router.events.off('routeChangeComplete', handleFinishedLoading);
router.events.off('routeChangeError', handleFinishedLoading);
};
}, [router]);
return mounted
? ReactDOM.createPortal(
<MemoizedNProgress loading={loading} />,

@ -25,7 +25,7 @@ import Link from 'next/link';
import { useEffect, useState } from 'react';
import { useInView } from 'react-intersection-observer';
import { defineMessages, useIntl } from 'react-intl';
import { useToasts } from 'react-toast-notifications';
// import { useToasts } from 'react-toast-notifications';
import useSWR, { mutate } from 'swr';
const messages = defineMessages({
@ -214,7 +214,7 @@ const RequestCard = ({ request, onTitleData }: RequestCardProps) => {
});
const intl = useIntl();
const { user, hasPermission } = useUser();
const { addToast } = useToasts();
// const { addToast } = useToasts();
const [isRetrying, setRetrying] = useState(false);
const [showEditModal, setShowEditModal] = useState(false);
const url =
@ -270,10 +270,10 @@ const RequestCard = ({ request, onTitleData }: RequestCardProps) => {
revalidate();
}
} catch (e) {
addToast(intl.formatMessage(messages.failedretry), {
autoDismiss: true,
appearance: 'error',
});
// addToast(intl.formatMessage(messages.failedretry), {
// autoDismiss: true,
// appearance: 'error',
// });
} finally {
setRetrying(false);
}

@ -15,7 +15,7 @@ import type { Collection } from '@server/models/Collection';
import axios from 'axios';
import { useCallback, useEffect, useState } from 'react';
import { defineMessages, useIntl } from 'react-intl';
import { useToasts } from 'react-toast-notifications';
// import { useToasts } from 'react-toast-notifications';
import useSWR from 'swr';
const messages = defineMessages({
@ -49,7 +49,7 @@ const CollectionRequestModal = ({
const [requestOverrides, setRequestOverrides] =
useState<RequestOverrides | null>(null);
const [selectedParts, setSelectedParts] = useState<number[]>([]);
const { addToast } = useToasts();
// const { addToast } = useToasts();
const { data, error } = useSWR<Collection>(`/api/v1/collection/${tmdbId}`, {
revalidateOnMount: true,
});
@ -213,24 +213,24 @@ const CollectionRequestModal = ({
);
}
addToast(
<span>
{intl.formatMessage(messages.requestSuccess, {
title: data?.name,
strong: (msg: React.ReactNode) => <strong>{msg}</strong>,
})}
</span>,
{ appearance: 'success', autoDismiss: true }
);
// addToast(
// <span>
// {intl.formatMessage(messages.requestSuccess, {
// title: data?.name,
// strong: (msg: React.ReactNode) => <strong>{msg}</strong>,
// })}
// </span>,
// { appearance: 'success', autoDismiss: true }
// );
} catch (e) {
addToast(intl.formatMessage(messages.requesterror), {
appearance: 'error',
autoDismiss: true,
});
// addToast(intl.formatMessage(messages.requesterror), {
// appearance: 'error',
// autoDismiss: true,
// });
} finally {
setIsUpdating(false);
}
}, [requestOverrides, data, onComplete, addToast, intl, selectedParts, is4k]);
}, [requestOverrides, data, onComplete, selectedParts, is4k]);
const hasAutoApprove = hasPermission(
[

@ -13,7 +13,7 @@ import type { MovieDetails } from '@server/models/Movie';
import axios from 'axios';
import { useCallback, useEffect, useState } from 'react';
import { defineMessages, useIntl } from 'react-intl';
import { useToasts } from 'react-toast-notifications';
// import { useToasts } from 'react-toast-notifications';
import useSWR, { mutate } from 'swr';
const messages = defineMessages({
@ -55,7 +55,7 @@ const MovieRequestModal = ({
const [isUpdating, setIsUpdating] = useState(false);
const [requestOverrides, setRequestOverrides] =
useState<RequestOverrides | null>(null);
const { addToast } = useToasts();
// const { addToast } = useToasts();
const { data, error } = useSWR<MovieDetails>(`/api/v1/movie/${tmdbId}`, {
revalidateOnMount: true,
});
@ -111,25 +111,25 @@ const MovieRequestModal = ({
: MediaStatus.PENDING
);
}
addToast(
<span>
{intl.formatMessage(messages.requestSuccess, {
title: data?.title,
strong: (msg: React.ReactNode) => <strong>{msg}</strong>,
})}
</span>,
{ appearance: 'success', autoDismiss: true }
);
// addToast(
// <span>
// {intl.formatMessage(messages.requestSuccess, {
// title: data?.title,
// strong: (msg: React.ReactNode) => <strong>{msg}</strong>,
// })}
// </span>,
// { appearance: 'success', autoDismiss: true }
// );
}
} catch (e) {
addToast(intl.formatMessage(messages.requesterror), {
appearance: 'error',
autoDismiss: true,
});
// addToast(intl.formatMessage(messages.requesterror), {
// appearance: 'error',
// autoDismiss: true,
// });
} finally {
setIsUpdating(false);
}
}, [data, onComplete, addToast, requestOverrides, hasPermission, intl, is4k]);
}, [data, onComplete, requestOverrides, hasPermission, is4k]);
const cancelRequest = async () => {
setIsUpdating(true);

@ -18,7 +18,7 @@ import type { TvDetails } from '@server/models/Tv';
import axios from 'axios';
import { useState } from 'react';
import { defineMessages, useIntl } from 'react-intl';
import { useToasts } from 'react-toast-notifications';
// import { useToasts } from 'react-toast-notifications';
import useSWR, { mutate } from 'swr';
const messages = defineMessages({
@ -69,7 +69,7 @@ const TvRequestModal = ({
is4k = false,
}: RequestModalProps) => {
const settings = useSettings();
const { addToast } = useToasts();
// const { addToast } = useToasts();
const editingSeasons: number[] = (editRequest?.seasons ?? []).map(
(season) => season.seasonNumber
);
@ -129,36 +129,36 @@ const TvRequestModal = ({
}
mutate('/api/v1/request?filter=all&take=10&sort=modified&skip=0');
addToast(
<span>
{selectedSeasons.length > 0
? intl.formatMessage(
alsoApproveRequest
? messages.requestApproved
: messages.requestedited,
{
title: data?.name,
strong: (msg: React.ReactNode) => <strong>{msg}</strong>,
}
)
: intl.formatMessage(messages.requestcancelled, {
title: data?.name,
strong: (msg: React.ReactNode) => <strong>{msg}</strong>,
})}
</span>,
{
appearance: 'success',
autoDismiss: true,
}
);
// addToast(
// <span>
// {selectedSeasons.length > 0
// ? intl.formatMessage(
// alsoApproveRequest
// ? messages.requestApproved
// : messages.requestedited,
// {
// title: data?.name,
// strong: (msg: React.ReactNode) => <strong>{msg}</strong>,
// }
// )
// : intl.formatMessage(messages.requestcancelled, {
// title: data?.name,
// strong: (msg: React.ReactNode) => <strong>{msg}</strong>,
// })}
// </span>,
// {
// appearance: 'success',
// autoDismiss: true,
// }
// );
if (onComplete) {
onComplete(MediaStatus.PENDING);
}
} catch (e) {
addToast(<span>{intl.formatMessage(messages.errorediting)}</span>, {
appearance: 'error',
autoDismiss: true,
});
// addToast(<span>{intl.formatMessage(messages.errorediting)}</span>, {
// appearance: 'error',
// autoDismiss: true,
// });
} finally {
if (onUpdating) {
onUpdating(false);
@ -208,21 +208,21 @@ const TvRequestModal = ({
if (onComplete) {
onComplete(response.data.media.status);
}
addToast(
<span>
{intl.formatMessage(messages.requestSuccess, {
title: data?.name,
strong: (msg: React.ReactNode) => <strong>{msg}</strong>,
})}
</span>,
{ appearance: 'success', autoDismiss: true }
);
// addToast(
// <span>
// {intl.formatMessage(messages.requestSuccess, {
// title: data?.name,
// strong: (msg: React.ReactNode) => <strong>{msg}</strong>,
// })}
// </span>,
// { appearance: 'success', autoDismiss: true }
// );
}
} catch (e) {
addToast(intl.formatMessage(messages.requesterror), {
appearance: 'error',
autoDismiss: true,
});
// addToast(intl.formatMessage(messages.requesterror), {
// appearance: 'error',
// autoDismiss: true,
// });
} finally {
if (onUpdating) {
onUpdating(false);

@ -1,92 +0,0 @@
import Modal from '@app/components/Common/Modal';
import useSettings from '@app/hooks/useSettings';
import { Permission, useUser } from '@app/hooks/useUser';
import globalMessages from '@app/i18n/globalMessages';
import { Transition } from '@headlessui/react';
import type { StatusResponse } from '@server/interfaces/api/settingsInterfaces';
import { Fragment, useEffect, useState } from 'react';
import { defineMessages, useIntl } from 'react-intl';
import useSWR from 'swr';
const messages = defineMessages({
appUpdated: '{applicationTitle} Updated',
appUpdatedDescription:
'Please click the button below to reload the application.',
reloadApp: 'Reload {applicationTitle}',
restartRequired: 'Server Restart Required',
restartRequiredDescription:
'Please restart the server to apply the updated settings.',
});
const StatusChecker = () => {
const intl = useIntl();
const settings = useSettings();
const { hasPermission } = useUser();
const { data, error } = useSWR<StatusResponse>('/api/v1/status', {
refreshInterval: 60 * 1000,
});
const [alertDismissed, setAlertDismissed] = useState(false);
useEffect(() => {
if (!data?.restartRequired) {
setAlertDismissed(false);
}
}, [data?.restartRequired]);
if (!data && !error) {
return null;
}
if (!data) {
return null;
}
return (
<Transition
as={Fragment}
enter="transition-opacity duration-300"
enterFrom="opacity-0"
enterTo="opacity-100"
leave="transition-opacity duration-300"
leaveFrom="opacity-100"
leaveTo="opacity-0"
appear
show={
!alertDismissed &&
((hasPermission(Permission.ADMIN) && data.restartRequired) ||
data.commitTag !== process.env.commitTag)
}
>
{hasPermission(Permission.ADMIN) && data.restartRequired ? (
<Modal
title={intl.formatMessage(messages.restartRequired)}
backgroundClickable={false}
onOk={() => {
setAlertDismissed(true);
if (data.commitTag !== process.env.commitTag) {
location.reload();
}
}}
okText={intl.formatMessage(globalMessages.close)}
>
{intl.formatMessage(messages.restartRequiredDescription)}
</Modal>
) : (
<Modal
title={intl.formatMessage(messages.appUpdated, {
applicationTitle: settings.currentSettings.applicationTitle,
})}
onOk={() => location.reload()}
okText={intl.formatMessage(messages.reloadApp, {
applicationTitle: settings.currentSettings.applicationTitle,
})}
backgroundClickable={false}
>
{intl.formatMessage(messages.appUpdatedDescription)}
</Modal>
)}
</Transition>
);
};
export default StatusChecker;

@ -1,6 +1,5 @@
import type { PublicSettingsResponse } from '@server/interfaces/api/settingsInterfaces';
import React from 'react';
import useSWR from 'swr';
export interface SettingsContextProps {
currentSettings: PublicSettingsResponse;
@ -34,19 +33,8 @@ export const SettingsProvider = ({
children,
currentSettings,
}: SettingsContextProps) => {
const { data, error } = useSWR<PublicSettingsResponse>(
'/api/v1/settings/public',
{ fallbackData: currentSettings }
);
let newSettings = defaultSettings;
if (data && !error) {
newSettings = data;
}
return (
<SettingsContext.Provider value={{ currentSettings: newSettings }}>
<SettingsContext.Provider value={{ currentSettings: {} }}>
{children}
</SettingsContext.Provider>
);

@ -0,0 +1,6 @@
/* Do not change, this code is generated from Golang structs */
export interface IndexRouteProps {
initialCount: number;
}

@ -1,27 +1,17 @@
import Layout from '@app/components/Layout';
import LoadingBar from '@app/components/LoadingBar';
import PWAHeader from '@app/components/PWAHeader';
import ServiceWorkerSetup from '@app/components/ServiceWorkerSetup';
import StatusChecker from '@app/components/StatusChecker';
import Toast from '@app/components/Toast';
import ToastContainer from '@app/components/ToastContainer';
import { InteractionProvider } from '@app/context/InteractionContext';
import type { AvailableLocale } from '@app/context/LanguageContext';
import { LanguageContext } from '@app/context/LanguageContext';
import { SettingsProvider } from '@app/context/SettingsContext';
import { UserContext } from '@app/context/UserContext';
import type { User } from '@app/hooks/useUser';
import '@app/styles/globals.css';
import { polyfillIntl } from '@app/utils/polyfillIntl';
import type { PublicSettingsResponse } from '@server/interfaces/api/settingsInterfaces';
import axios from 'axios';
import type { AppInitialProps, AppProps } from 'next/app';
import App from 'next/app';
import Head from 'next/head';
import type { AppProps } from 'next/app';
import { useEffect, useState } from 'react';
import { IntlProvider } from 'react-intl';
import { ToastProvider } from 'react-toast-notifications';
import { SWRConfig } from 'swr';
import Index from './index';
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const loadLocaleData = (locale: AvailableLocale): Promise<any> => {
@ -82,7 +72,7 @@ const loadLocaleData = (locale: AvailableLocale): Promise<any> => {
// Custom types so we can correctly type our GetInitialProps function
// with our combined user prop
// This is specific to _app.tsx. Other pages will not need to do this!
type NextAppComponentType = typeof App;
type MessagesType = Record<string, string>;
interface ExtendedAppProps extends AppProps {
@ -96,18 +86,12 @@ if (typeof window === 'undefined') {
global.Intl = require('intl');
}
let headers = {};
const CoreApp: Omit<NextAppComponentType, 'origGetInitialProps'> = ({
Component,
pageProps,
router,
export default function App({
user,
currentSettings,
messages,
locale,
currentSettings,
}: ExtendedAppProps) => {
let component: React.ReactNode;
}: ExtendedAppProps) {
const [loadedMessages, setMessages] = useState<MessagesType>(messages);
const [currentLocale, setLocale] = useState<AvailableLocale>(locale);
@ -115,151 +99,23 @@ const CoreApp: Omit<NextAppComponentType, 'origGetInitialProps'> = ({
loadLocaleData(currentLocale).then(setMessages);
}, [currentLocale]);
if (router.pathname.match(/(login|setup|resetpassword)/)) {
component = <Component {...pageProps} />;
} else {
component = (
<Layout>
<Component {...pageProps} />
</Layout>
);
}
return (
<SWRConfig
value={{
fetcher: (url) =>
axios
.get(url, {
headers,
})
.then((res) => res.data),
fallback: {
'/api/v1/auth/me': user,
},
}}
>
<LanguageContext.Provider value={{ locale: currentLocale, setLocale }}>
<IntlProvider
locale={currentLocale}
defaultLocale="en"
messages={loadedMessages}
>
<LoadingBar />
<SettingsProvider currentSettings={currentSettings}>
<InteractionProvider>
<ToastProvider components={{ Toast, ToastContainer }}>
<Head>
<title>{currentSettings.applicationTitle}</title>
<meta
name="viewport"
content="initial-scale=1, viewport-fit=cover, width=device-width"
></meta>
<PWAHeader
applicationTitle={currentSettings.applicationTitle}
/>
</Head>
<StatusChecker />
<ServiceWorkerSetup />
<UserContext initialUser={user}>{component}</UserContext>
</ToastProvider>
</InteractionProvider>
</SettingsProvider>
</IntlProvider>
</LanguageContext.Provider>
</SWRConfig>
);
};
CoreApp.getInitialProps = async (initialProps) => {
const { ctx, router } = initialProps;
let user: User | undefined = undefined;
let currentSettings: PublicSettingsResponse = {
initialized: false,
applicationTitle: '',
applicationUrl: '',
hideAvailable: false,
movie4kEnabled: false,
series4kEnabled: false,
localLogin: true,
region: '',
originalLanguage: '',
partialRequestsEnabled: true,
cacheImages: false,
vapidPublic: '',
enablePushRegistration: false,
locale: 'en',
emailEnabled: false,
newPlexLogin: true,
};
if (ctx.res) {
// Check if app is initialized and redirect if necessary
const response = await axios.get<PublicSettingsResponse>(
`http://localhost:${process.env.PORT || 5055}/api/v1/settings/public`
);
currentSettings = response.data;
const initialized = response.data.initialized;
if (!initialized) {
if (!router.pathname.match(/(setup|login\/plex)/)) {
ctx.res.writeHead(307, {
Location: '/setup',
});
ctx.res.end();
}
} else {
try {
const authHeader = ctx.req?.headers['x-api-key'];
if (ctx.req && authHeader) {
headers = { ...ctx.req?.headers };
} else if (ctx.req && ctx.req.headers.cookie) {
headers = { cookie: ctx.req.headers.cookie };
}
// Attempt to get the user by running a request to the local api
const response = await axios.get<User>(
`http://localhost:${process.env.PORT || 5055}/api/v1/auth/me`,
{
headers,
}
);
user = response.data;
if (router.pathname.match(/(setup|login)/)) {
ctx.res.writeHead(307, {
Location: '/',
});
ctx.res.end();
}
} catch (e) {
// If there is no user, and ctx.res is set (to check if we are on the server side)
// _AND_ we are not already on the login or setup route, redirect to /login with a 307
// before anything actually renders
if (!router.pathname.match(/(login|setup|resetpassword)/)) {
ctx.res.writeHead(307, {
Location: '/login',
});
ctx.res.end();
}
}
}
}
// Run the default getInitialProps for the main nextjs initialProps
const appInitialProps: AppInitialProps = await App.getInitialProps(
initialProps
<LanguageContext.Provider value={{ locale: currentLocale, setLocale }}>
<IntlProvider
locale={currentLocale}
defaultLocale="en"
messages={loadedMessages}
>
<LoadingBar />
<SettingsProvider currentSettings={currentSettings}>
<InteractionProvider>
<ServiceWorkerSetup />
<Layout>
<Index />
</Layout>
</InteractionProvider>
</SettingsProvider>
</IntlProvider>
</LanguageContext.Provider>
);
const locale = user?.settings?.locale
? user.settings.locale
: currentSettings.locale;
const messages = await loadLocaleData(locale as AvailableLocale);
await polyfillIntl(locale);
return { ...appInitialProps, user, messages, locale, currentSettings };
};
export default CoreApp;
}

@ -1,32 +1,13 @@
import type { DocumentContext, DocumentInitialProps } from 'next/document';
import Document, { Head, Html, Main, NextScript } from 'next/document';
class MyDocument extends Document {
static async getInitialProps(
ctx: DocumentContext
): Promise<DocumentInitialProps> {
const initialProps = await Document.getInitialProps(ctx);
return initialProps;
}
render(): JSX.Element {
return (
<Html>
<Head>
<link rel="preconnect" href="https://fonts.gstatic.com" />
<link
rel="stylesheet"
href="https://fonts.googleapis.com/css2?family=Inter:wght@100..900&display=swap"
/>
</Head>
<body>
<Main />
<NextScript />
</body>
</Html>
);
}
import App from './_app';
export default function Document() {
return (
<App
user={{}}
currentSettings={{
applicationTitle: '123',
}}
locale="en"
/>
);
}
export default MyDocument;

@ -1,29 +1,39 @@
{
"compilerOptions": {
"target": "es5",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"target": "ES6",
"lib": ["DOM", "DOM.Iterable", "ESNext"],
"module": "ESNext",
"skipLibCheck": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "node",
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"strictPropertyInitialization": false,
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"useUnknownInCatchVariables": false,
"incremental": true,
"noEmit": true,
"jsx": "react-jsx",
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
"baseUrl": "src",
"paths": {
"@server/*": ["../server/*"],
"@app/*": ["*"]
}
"@server/*": [
"../server/*"
],
"@app/*": [
"*"
]
},
"allowJs": true,
"forceConsistentCasingInFileNames": true,
"incremental": true,
"esModuleInterop": true
},
"include": ["next-env.d.ts", "src/**/*.ts", "src/**/*.tsx"],
"exclude": ["node_modules"]
"include": [
"next-env.d.ts",
"src/**/*.ts",
"src/**/*.tsx"
],
"exclude": [
"node_modules"
]
}

14611
yarn.lock

File diff suppressed because it is too large Load Diff
Loading…
Cancel
Save