From c4ac357ef4cdd7a2c610260db46a4f0c325cd785 Mon Sep 17 00:00:00 2001 From: sct Date: Mon, 2 Nov 2020 12:11:28 +0000 Subject: [PATCH] feat: radarr edit/create modal/backend functionality --- overseerr-api.yml | 96 +++- package.json | 4 +- public/images/radarr_logo.png | Bin 0 -> 22849 bytes public/images/sonarr_logo.png | Bin 0 -> 21013 bytes server/lib/settings.ts | 3 + server/routes/settings.ts | 62 +++ src/components/Common/Button/index.tsx | 16 +- src/components/Common/Modal/index.tsx | 10 +- src/components/Layout/Sidebar/index.tsx | 17 + src/components/Settings/RadarrModal.tsx | 464 +++++++++++++++++++ src/components/Settings/SettingsPlex.tsx | 4 +- src/components/Settings/SettingsServices.tsx | 247 ++++++++++ src/pages/settings/services.tsx | 14 + yarn.lock | 47 +- 14 files changed, 968 insertions(+), 16 deletions(-) create mode 100644 public/images/radarr_logo.png create mode 100644 public/images/sonarr_logo.png create mode 100644 src/components/Settings/RadarrModal.tsx create mode 100644 src/components/Settings/SettingsServices.tsx create mode 100644 src/pages/settings/services.tsx diff --git a/overseerr-api.yml b/overseerr-api.yml index c9b1cc723..43e0518de 100644 --- a/overseerr-api.yml +++ b/overseerr-api.yml @@ -116,6 +116,9 @@ components: activeProfileId: type: number example: 1 + activeProfileName: + type: string + example: 720p/1080p activeDirectory: type: string example: '/movies' @@ -125,16 +128,21 @@ components: minimumAvailability: type: string example: 'In Cinema' + isDefault: + type: boolean + example: false required: - name - hostname - port - apiKey - useSsl - - activeProfile + - activeProfileId + - activeProfileName - activeDirectory - is4k - minimumAvailability + - isDefault SonarrSettings: type: object properties: @@ -162,12 +170,18 @@ components: activeProfileId: type: number example: 1 + activeProfileName: + type: string + example: 720p/1080p activeDirectory: type: string example: '/tv/' activeAnimeProfileId: type: number nullable: true + activeAnimeProfileName: + type: string + example: 720p/1080p activeAnimeDirectory: type: string nullable: true @@ -177,6 +191,9 @@ components: enableSeasonFolders: type: boolean example: false + isDefault: + type: boolean + example: false required: - name - hostname @@ -184,9 +201,11 @@ components: - apiKey - useSsl - activeProfileId + - activeProfileName - activeDirectory - is4k - enableSeasonFolders + - isDefault PublicSettings: type: object properties: @@ -704,6 +723,15 @@ components: type: string twitterId: type: string + ServiceProfile: + type: object + properties: + id: + type: number + example: 1 + name: + type: string + example: 720p/1080p securitySchemes: cookieAuth: @@ -877,6 +905,50 @@ paths: application/json: schema: $ref: '#/components/schemas/RadarrSettings' + /settings/radarr/test: + post: + summary: Test radarr configuration + description: Test if the provided Radarr congifuration values are valid. Returns profiles and root folders on success + tags: + - settings + requestBody: + required: true + content: + application/json: + schema: + type: object + properties: + hostname: + type: string + example: '127.0.0.1' + port: + type: number + example: 7878 + apiKey: + type: string + example: yourapikey + useSsl: + type: boolean + example: false + baseUrl: + type: string + required: + - hostname + - port + - apiKey + - useSsl + responses: + '200': + description: Succesfully connected to Radarr instance + content: + application/json: + schema: + type: object + properties: + profiles: + type: array + items: + $ref: '#/components/schemas/ServiceProfile' /settings/radarr/{radarrId}: put: summary: Update existing radarr instance @@ -922,6 +994,28 @@ paths: application/json: schema: $ref: '#/components/schemas/RadarrSettings' + /settings/radarr/{radarrId}/profiles: + get: + summary: Retrieve available profiles for the Radarr instance + description: Returns an array of profile available on the Radarr server instance in JSON format + tags: + - settings + parameters: + - in: path + name: radarrId + required: true + schema: + type: integer + description: Radarr Instance ID + responses: + '200': + description: Returned list of profiles + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/ServiceProfile' /settings/sonarr: get: summary: Get all sonarr settings diff --git a/package.json b/package.json index 0dd14e9ef..b4caade12 100644 --- a/package.json +++ b/package.json @@ -45,7 +45,8 @@ "uuid": "^8.3.0", "winston": "^3.3.3", "xml2js": "^0.4.23", - "yamljs": "^0.3.0" + "yamljs": "^0.3.0", + "yup": "^0.29.3" }, "devDependencies": { "@babel/cli": "^7.11.6", @@ -65,6 +66,7 @@ "@types/uuid": "^8.3.0", "@types/xml2js": "^0.4.5", "@types/yamljs": "^0.2.31", + "@types/yup": "^0.29.9", "@typescript-eslint/eslint-plugin": "^4.0.0", "@typescript-eslint/parser": "^3.10.1", "babel-plugin-react-intl": "^8.2.2", diff --git a/public/images/radarr_logo.png b/public/images/radarr_logo.png new file mode 100644 index 0000000000000000000000000000000000000000..f192d0bc13982850c61ca13b9b11ebbec1f69225 GIT binary patch literal 22849 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 literal 0 HcmV?d00001 diff --git a/public/images/sonarr_logo.png b/public/images/sonarr_logo.png new file mode 100644 index 0000000000000000000000000000000000000000..3311ac54ee9382d5fa46c9975f540448722f8778 GIT binary patch literal 21013 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

}xadO}w zrOzZATI{(d0wCg=o#@s^hgzCm{)eKX`ju?nw}G{2XxdPHQP;W4AZm#}sBurotbC|o z{n~;>+1yae)p*lHV_acU9^Nt716EfGOe&v;&FJU3SNg-=t@3|ZNp&5-Du4X zyXs zCQ!#hXidYEuNTuMS9OM}15+N1Qwf%y!ra_&2+u6Q;%)5Lk`e+#Y%&=aA{9%ta$bq8 zbI|pTXF9Kmx?iGvO>^z8zE(__Kiz0Y&07j#>btBHpz3nMBjCyPc8ttcA!lp?8{+@@ zz4JS7x(UOytiPT?z4w=I=QY>zeuD7BG^= zqFzh`KMh!X^~3r3BHu(y)H$tU3a3@8-B)r&MV{G2Oblv?I?5kWK0Xy|p`1kC@`bTN zj6&mFMo;17Y=Cfl113Gc?b%T`WiS8HFU~{N`oKVj8bGIWf91-0C}e(P-Q|>KxpTVE zL-`6EzHsN%(_Z4fIIDYx7b=?tLg2b)Zg!%w<*2NWnCPf6x%3z&Pb5ybSHY$SC-jkH z(@eNkBL;)}q7Jph&*<5ra8T8dxe(}iK8#Z9V0$jS^u8;={)jT@Zs#Oj(-~R(dnc3ldu$)$R<4DB97AHC)2a}|FiBjsIvvR7H?vwgP zeYYvD){XPmXaK4>F6(2{OxDJ>@EVq+oMotX7Xd)vu9)CJ-zFTh(Q^9@sQK{@!EbaI83R>t$y2 zr8gC+K*Uy3zfL1;#@E06lie!_ZK{gRQFxX#Iyxjvshn1Eksve6D_W)>>?QJu6g&9( z@|H?vzcneP`&c~Vx-Xvp!bWxA7+OsbWe#fn!K^3b(X~&TF@qpEFt?;a-$S`2m6YJi z?kpMtn&gcnw}K+F)Lg=}nrJM)IC*-Ln5>1OcCg%Q^!S9g$*-BMm1_N& znj>q(4MU3vsk7p5V9~V+k7+fHN-0Mb7M%tyu;_wHJx@lRB3+klZ?6Y(-nA=mkrOTA zR=h$&=-H|kGwVY_Ts?#&N?a6yBO3GvllbdPIl}q!R!nZGN#zB8cFJN7=sTvAMM_i{ zxGIb)&lu2ZDt4byXfE#+)U4;KMx_}jrqC^SDzS{q4i*goJ@3jcLI&>|b*qeDM#9-7 zpgpp_S-ak5O=i(XxMl#7Bwc8bS-z$kMPry^lJXkY_IWXmvSN4l0Hz*$bcF&}I+}b+ zPb#={!6T^cE_;oy;goovm(F*UZCz8OY!^~ z1X9N>NwN}d@2oNtrpT!(OK8G}sS&^Th4UL$9>cEpK=&K({iVl|JKThcR9)3uW^!s5 z`rKfQm}@8Jyzah2V5UU9rHu=<-q36{ZU*3u&9+)XlRi8v9D!l0C3o;e=eol~l&CbL z=scj)&f3|Sx3uutQvBwsaM)XVTxQv^5+kiFPECL7DMc%|G;lF_IUh?wmc2KO!Es#h zQ#*YvCQF4?x@mV8DQ7%309@Y2?}biuR+%tk7*uSHZXLvp((zh$S&wYPjF}|6vSrti zfQx>Rv3YWHlLhmW!Fg%V{Zl}eY6O~#%wr@#ao2TN*Es^4$2!c)n>)4*Uc0yA0*jW) zk}^iK<~vjN!^69DdVKwmQEj6Kdfrax!VWyzO_7*^BJwreu~I={B1A&00I6><7kE@s zMc^JOK}@x$F$-E6 z^OSRyYL6mam}(Ex=9|{-Ch7z0y4@=(E+>d&7L~XT@w*1t&U3=s>G|l65m%evnzd#?l&%6WO*{H(q{a z_W+3+uWbJ!X24YVi5Lpq25Y>ftF=Q=Lkh^-7bh+Pmmpp_)DSO(H-2IFFIVE=Gf)Vx z{lf3#?Iap7C|G({wk#QHlMiE5h%$`=c;!$r2lunI^&OtfIkC-EYBzIN;aDxHDZpE%XBUptQdsgk|K(Tb z?|K<0)L|;!_w1C!6A+z|8*!rEq~vF~wU@V#xu5BE_iYEg-J)y7< z_hUxpmcDPj^5vEF6jL2Kyy~r|OUWh=)I8B}tSXPBdU_U`=6f*BB&G*149)iemIddo zj-Jj?rw6w$L*_NHzL|+~d{dwxxH1g1HqE5q$2;2D^wtO899l^stsBmN;fL7e1MDl@ z7z%?u=sH@GzpSM2WJ*r;VV08=zTSWR!;O&5);!zUk_Xk>(=VA17PaGifK1Hmyzzm` zU63-Ha+bg@XbfnM4g(f%w~hc%x$*5UoTtqoGBa=b@DD$Rrh2vW7|3d#?Wsm<&dBSR zl@y+;pIyWzEhEUhzO3yKaD?FTwoaB?C!!lyAH1oa#8R_{4r4BFp|pR-*VtK#+b{`^ z(2c&P>1s@dDVUokt(6l#JY+(zbv7Cp!5z$DWmM0G0-a3`d@eMH=K_U5c!A$+i4_K7 z;adD=^uSZs711C@)=lR>_?v)dI*^AiGImTvb9WS%tY3~%SCg{4)pC-;gMWLkMQgu3 zacvItCGW0RV#D1x=h1?0>(#k}N)K6eZOlZOF@Z|x#FR>p4&9D7yOrcCWbWBhN`n>f zLcyLE?akF!M~z5pXGWvd$6mhe;E(=eoAyD@@L=mvIe!^R;is!-klP=%nHuvO)GN;7K;RjB8+ zqh>ASWbC(SZG$$xcfJdk$kbUT1 zCoOMG>J9ZlYQyVKsJ47+e|@nQhD*}(h;rE;*pwAqQ~?JsBRks-MStN`@FOa^5_?re zHzpTd3B_YisK$QKSB*zb1TH^jp$VXJAQ{4RfwTr^1q^IGL)#@l^07G@j9Q=ep~5-g(*Em2p(iXEkHS*}Jo)MIpJx`&O1Pp_;^` z@b%e|J-rZ8rZ%);oHttz39-gXe|9|Hya3ud#aH}{~DsSVdU^yk#B4er9@zDQDd%@WN@ z*{%m%5+NoktJx!9?_6nW27YS8kDaTEy0))I>-HPJH}BALwsXd`R_PEf&Lv&jNMo`X z$$m0Bo$lJ!=txUdYio70)9eJ-o6qX;nUKeZ}vdb3!6l=ZMustx$9BANyYTW%t3$ zF#cHExT}fQB|!;DXdBLJB;U(Yu%)ooNSW|mfs=cp;jt4ZckDQN{P;s>kH7ZX@#pvL zIJx86lSfB9EwoE%bD#1-kd0w!8tcKe0QfdLqSO5tpTW!=}>8Els#i$5k*nS6V&ebC@iik zQY+*xgy;v2%#@Z-^kF+auY97vDtBU`72ln=-~GiGcbhWMi)DsT5OQZ8Ya6EF;(4C+Aajn}eq1DpShzHGFaC+bAeUF3C#dX0tTap=xeX6z^=)_!iX79cC_jGgw znp$#vJ*4NmH`Eibw9fQ*;E;8BVUxEx)c*t5inX^rbM4cm?#fQwNYd2N7+YiU%mjAr zjYvuT_2qOSuV8o;nWXTq*W5B18=g_nL`&1;uAXn8{oOx3xcY&D>4|4+m9F>J@bCoA z86{0=J4fcBE+(+~(37X|uoefoHfek>P}-l^Rvf{dsPEaaZO_(NQ!S$0L$!s&3k?bs z>7MW0g7Y!a_|BiaQTXokjGO*tbNS?SxD|VGvMpIsc=0m&uhT>WU65HU`};j8ug4c} zBo^vN=2b=A2SMwg@)=j0Dt*kYH0p)b*%PSTbMM}Nk^ODDZ^9(yCrRVZFOjn!Kl{)T zFH++2_OWKIy0JIZKvHSUHy^ugf9JQ5!cLCwG|8f!I)#g~^+D!*|1w0!^^QCr304s$ z&q1b0xyIRSLcSN#{-K@QNqISjCD|tAN-JVvbjR^C(0)G1cMIctZ!wcgWYV~86>`lJ z$M+PFZoQ zZNCgD*qlql>2S6rZq2;s`MZBc`mFIgMPiw;lu(iz&tZ~ke)f;oJQqObd56IDU#GTD z-u{8^{WL18_FpH+&-AGhzW6H`S*GY|^eb!M&|h*A`(Z$TrHT$D*e`VL1j$WWk0-T# zsjCkZuIJ?AGR;I3Q&dwgFO3`1$H?-T9Zis*LIb*#OjY@7Ap6elRe6fHP}<<2DITEf z@@u$Ht4Le+QDbA$3-kYeD~XlB&eE)|^wq02+;woZVvZ}K({xr>qh<6EtngrhE09=! z$VbR{Qpb4eSJ0om_n{^eQbqS^qJs(hl`j^pQoaWfU5Qf?k6|kafhcoV_y0#u%bCGS1jF$Ik}eCg3fLJ|y;tTAw?tl~^2;B)80? zn0$#yG0gVT*p}STq=z&f_l|zQe1CV>L2S3@ln*v|CYng^aJzLjwC38|zv~Yswhc@u zzeu76mQ;Dt`Zjs}lY2b4lP7XFOpFGmNijeA>aYKz>wo>~=`RU{E}(PHx6o5XyrqS< zVhOOyr5v{8JDijs*@|n9Tphb`b9ACXHu~PVs|Z^%gr1>)TPqDXz>S!FO_b|IJ;j%# zXFKHU#6`YWxSmmnYb7mK$k*S3T{PHWN^cx)?xneE8z0=>_2D|jXS9rN_Suzhv}-Rg z&n$mmV`R5U{^A`#+nfh0X~L^`$H@4|`?uj;4IY6bSw4r55@c^ZE|>5bv?3p<*Ri!S z)mU72)MRPg>LzXT7e9|fO&BQ%ss6eV{rZ{Pc8@Dv+H8QxV}QZ7}3+Rcl0VG0nRXOxejPl`_3=_TFntK(pKQu zX*pUZ2iJ3SO?;UsQ#Z>J2ynggq{Px>9#Y2`Sx_!MT}3-_E6JqbPrP*5_ML~xxC0Ds z*9Io1;q=8xo|b)!n(j+QC%anr_Rqbfw2d?>qC=qI_}72ScLx>O0JX>$P3jxp>ZpSB znVzC97rjQlrvNFUE3OpqVnAol6*!ZFh^{n4vTVIO?IMqwPPNk#2?XF>t?S@}-|;a$ zcN(xY0clKS>m|OMRI)l6iEB0`>iC_+gmm;I1fTeQ|4F{15?KovL>BtQUVg<-*KUH@ z;3#xyo=)@aX%~Gw5mj1&W0G>@T(zMKtXEGU?zI=pV7}cAB{uY;awmT5aul87CL(mulRgpafWZ#mIp5kT^y zgR3{T4(=+Y?Z1Uy%jD)}W9_|io34^T;#(_m#lUO?Pv5G6@X70J!iS#on4JTR8U)+!%+|R|uMtx&j*eNYCVHAp zFz0Vu`>PjUjZNVjf%3MdeZ=F*+R9;^-}j&2DJr`2M{f@Y97e%0y3OZub__Q`BgMcq zIL4X88|R%fDd7q!fdPg~`c|2-voL=Z}B^ee_)xYJfcb~fQHs~7yWk`yy!{{WxyH8m_zO z+>gFxRWi46M+OKjA)Jd}$MYgcNoa8-U1u?tMnTfC{&IyTqNwjl7{4+oL+W1BoS9-6 zpo%*RUxmV9qU(g_OCot%+771~bo=(zy-zm_T85-Aiix)F#geDsuuWD_B1*{!KgDHK zizn*){C~M4*P5&4YyxMs9fzcLUU@5m6w0(qbDVsk2=8jHhGwJIpOl0^O-jNUV6~|4F-) z$4Lif19B^obr8Mk+I>F@^13;J-cd>@NzuEp{AfxdYXY(?->Im?f|1(`6t`LM2JFOd ztvr)a4by|M>O8coEbY=C zw};J^zA%*9AXoLC`y!I0C!dv;^4FzAe_hh~>w0Epoxjd!d6VD4;xhqg@HpRB+H2ia zu;u!+kK;H4*ih?aPi}3&El+$mUrh8uc`ucXC^LQ?dpYma^A2jN)|33@q{{M4zpt=^ zG_SuV?PEIbn*p50GuGdCub*{GB{8A5jFiZH#=+5x`0IT9QPlhEVxo^L)!n^Sz%eK9 zEs>8)^t7@gIMBcAHEO-;8nlbtAd9*_4f;7CaUB{&uAC~tw&z_RJ(U=&_xqrvJFn`s zw2$-H;YQK>XlzDg9B?VAzs`HD9Lv7uuj|tbQu^x&6#mR^5%k|4Le{fsAMbIz?=?9; z9t>o+Fi|(=BD?D)0F?1yGV7P8^4E1#VxkVu3;yV(Z%HbaZ-z(3d~3jZm^fP zMbbXm!{3&YnET@iGN-sESR(uZ7Qif9oL&wU2rv z?ay)ei}|3~C-1c8{~3l{UGa5Hsy*XQNxb{2>$JgQwJaI8#AWMoRXa?*Zrsq7{!Bxz z1Y>K0d_UAn!WpV48uPl?e?zmjcJRWaL;2202 zWY+oXNK;X`k9d8G0$X zp?)z*;hM`{SL2t{KGlcpuPC2|s2LJ#7 literal 0 HcmV?d00001 diff --git a/server/lib/settings.ts b/server/lib/settings.ts index 238d5817e..148f395aa 100644 --- a/server/lib/settings.ts +++ b/server/lib/settings.ts @@ -25,8 +25,10 @@ interface DVRSettings { useSsl: boolean; baseUrl?: string; activeProfileId: number; + activeProfileName: string; activeDirectory: string; is4k: boolean; + isDefault: boolean; } export interface RadarrSettings extends DVRSettings { @@ -35,6 +37,7 @@ export interface RadarrSettings extends DVRSettings { export interface SonarrSettings extends DVRSettings { activeAnimeProfileId?: number; + activeAnimeProfileName?: string; activeAnimeDirectory?: string; enableSeasonFolders: boolean; } diff --git a/server/routes/settings.ts b/server/routes/settings.ts index 395e38381..2c56b3c5a 100644 --- a/server/routes/settings.ts +++ b/server/routes/settings.ts @@ -9,6 +9,9 @@ import { getRepository } from 'typeorm'; import { User } from '../entity/User'; import PlexAPI, { PlexLibrary } from '../api/plexapi'; import jobPlexSync from '../job/plexsync'; +import SonarrAPI from '../api/sonarr'; +import RadarrAPI from '../api/radarr'; +import logger from '../logger'; const settingsRoutes = Router(); @@ -132,6 +135,35 @@ settingsRoutes.post('/radarr', (req, res) => { return res.status(201).json(newRadarr); }); +settingsRoutes.post('/radarr/test', async (req, res, next) => { + try { + const radarr = new RadarrAPI({ + apiKey: req.body.apiKey, + url: `${req.body.useSsl ? 'https' : 'http'}://${req.body.hostname}:${ + req.body.port + }${req.body.baseUrl ?? ''}/api`, + }); + + const profiles = await radarr.getProfiles(); + const folders = await radarr.getRootFolders(); + + return res.status(200).json({ + profiles, + rootFolders: folders.map((folder) => ({ + id: folder.id, + path: folder.path, + })), + }); + } catch (e) { + logger.error('Failed to test Radarr', { + label: 'Radarr', + message: e.message, + }); + + next({ status: 500, message: 'Failed to connect to Radarr' }); + } +}); + settingsRoutes.put<{ id: string }>('/radarr/:id', (req, res) => { const settings = getSettings(); @@ -154,6 +186,36 @@ settingsRoutes.put<{ id: string }>('/radarr/:id', (req, res) => { return res.status(200).json(settings.radarr[radarrIndex]); }); +settingsRoutes.get<{ id: string }>('/radarr/:id/profiles', async (req, res) => { + const settings = getSettings(); + + const radarrSettings = settings.radarr.find( + (r) => r.id === Number(req.params.id) + ); + + if (!radarrSettings) { + return res + .status(404) + .json({ status: '404', message: 'Settings instance not found' }); + } + + const radarr = new RadarrAPI({ + apiKey: radarrSettings.apiKey, + url: `${radarrSettings.useSsl ? 'https' : 'http'}://${ + radarrSettings.hostname + }:${radarrSettings.port}${radarrSettings.baseUrl ?? ''}/api`, + }); + + const profiles = await radarr.getProfiles(); + + return res.status(200).json( + profiles.map((profile) => ({ + id: profile.id, + name: profile.name, + })) + ); +}); + settingsRoutes.delete<{ id: string }>('/radarr/:id', (req, res) => { const settings = getSettings(); diff --git a/src/components/Common/Button/index.tsx b/src/components/Common/Button/index.tsx index ac39b1adb..b1f9d05af 100644 --- a/src/components/Common/Button/index.tsx +++ b/src/components/Common/Button/index.tsx @@ -5,7 +5,8 @@ export type ButtonType = | 'primary' | 'danger' | 'warning' - | 'success'; + | 'success' + | 'ghost'; interface ButtonProps extends ButtonHTMLAttributes { buttonType?: ButtonType; @@ -30,22 +31,27 @@ const Button: React.FC = ({ break; case 'danger': buttonStyle.push( - 'text-white bg-red-600 hover:bg-red-500 focus:border-red-700 focus:shadow-outline-red active:bg-red-700' + 'text-white bg-red-600 hover:bg-red-500 focus:border-red-700 focus:shadow-outline-red active:bg-red-700 disabled:opacity-50' ); break; case 'warning': buttonStyle.push( - 'text-white bg-orange-500 hover:bg-orange-400 focus:border-orange-700 focus:shadow-outline-orange active:bg-orange-700' + 'text-white bg-orange-500 hover:bg-orange-400 focus:border-orange-700 focus:shadow-outline-orange active:bg-orange-700 disabled:opacity-50' ); break; case 'success': buttonStyle.push( - 'text-white bg-green-400 hover:bg-green-300 focus:border-green-700 focus:shadow-outline-green active:bg-green-700' + 'text-white bg-green-400 hover:bg-green-300 focus:border-green-700 focus:shadow-outline-green active:bg-green-700 disabled:opacity-50' + ); + break; + case 'ghost': + buttonStyle.push( + 'text-white bg-transaprent border border-cool-gray-600 hover:border-cool-gray-200 focus:border-cool-gray-100 active:border-cool-gray-100 disabled:opacity-50' ); break; default: buttonStyle.push( - 'leading-5 font-medium rounded-md text-gray-200 bg-cool-gray-500 hover:bg-cool-gray-400 hover:text-white focus:border-blue-300 focus:shadow-outline-blue active:text-gray-200 active:bg-cool-gray-400' + 'leading-5 font-medium rounded-md text-gray-200 bg-cool-gray-500 hover:bg-cool-gray-400 hover:text-white focus:border-blue-300 focus:shadow-outline-blue active:text-gray-200 active:bg-cool-gray-400 disabled:opacity-50' ); } diff --git a/src/components/Common/Modal/index.tsx b/src/components/Common/Modal/index.tsx index 522488f7d..a5427c5f7 100644 --- a/src/components/Common/Modal/index.tsx +++ b/src/components/Common/Modal/index.tsx @@ -103,7 +103,7 @@ const Modal: React.FC = ({ item && ( = ({ {iconSvg} )} -

+
{title && (

= ({

{children && ( -
-

- {children} -

+
+ {children}
)} {(onCancel || onOk || onSecondary || onTertiary) && ( diff --git a/src/components/Layout/Sidebar/index.tsx b/src/components/Layout/Sidebar/index.tsx index af54340ce..fcc98e802 100644 --- a/src/components/Layout/Sidebar/index.tsx +++ b/src/components/Layout/Sidebar/index.tsx @@ -8,6 +8,7 @@ import { useUser, Permission } from '../../../hooks/useUser'; const messages = defineMessages({ dashboard: 'Dashboard', requests: 'Requests', + users: 'Users', settings: 'Settings', }); @@ -68,6 +69,22 @@ const SidebarLinks: SidebarLinkProps[] = [ ), activeRegExp: /^\/requests/, }, + { + href: '/users', + messagesKey: 'users', + svgIcon: ( + + + + ), + activeRegExp: /^\/users/, + requiredPermission: Permission.MANAGE_USERS, + }, { href: '/settings', messagesKey: 'settings', diff --git a/src/components/Settings/RadarrModal.tsx b/src/components/Settings/RadarrModal.tsx new file mode 100644 index 000000000..425e3ce8e --- /dev/null +++ b/src/components/Settings/RadarrModal.tsx @@ -0,0 +1,464 @@ +import React, { useState, useEffect, useCallback, useRef } from 'react'; +import Transition from '../Transition'; +import Modal from '../Common/Modal'; +import { Formik, Field } from 'formik'; +import type { RadarrSettings } from '../../../server/lib/settings'; +import * as Yup from 'yup'; +import axios from 'axios'; +import { useToasts } from 'react-toast-notifications'; + +interface TestResponse { + profiles: { + id: number; + name: string; + }[]; + rootFolders: { + id: number; + path: string; + }[]; +} + +interface RadarrModalProps { + radarr: RadarrSettings | null; + onClose: () => void; + onSave: () => void; +} + +const RadarrModal: React.FC = ({ + onClose, + radarr, + onSave, +}) => { + const initialLoad = useRef(false); + const { addToast } = useToasts(); + const [isValidated, setIsValidated] = useState(radarr ? true : false); + const [isTesting, setIsTesting] = useState(false); + const [testResponse, setTestResponse] = useState({ + profiles: [], + rootFolders: [], + }); + const RadarrSettingsSchema = Yup.object().shape({ + hostname: Yup.string().required('You must provide a hostname/IP'), + port: Yup.number().required('You must provide a port'), + apiKey: Yup.string().required('You must provide an API Key'), + rootFolder: Yup.string().required('You must select a root folder'), + activeProfileId: Yup.string().required('You must select a profile'), + }); + + const testConnection = useCallback( + async ({ + hostname, + port, + apiKey, + baseUrl, + useSsl = false, + }: { + hostname: string; + port: number; + apiKey: string; + baseUrl?: string; + useSsl?: boolean; + }) => { + setIsTesting(true); + try { + const response = await axios.post( + '/api/v1/settings/radarr/test', + { + hostname, + apiKey, + port, + baseUrl, + useSsl, + } + ); + + setIsValidated(true); + setTestResponse(response.data); + if (initialLoad.current) { + addToast('Radarr connection established!', { + appearance: 'success', + autoDismiss: true, + }); + } + } catch (e) { + setIsValidated(false); + if (initialLoad.current) { + addToast('Failed to connect to Radarr server', { + appearance: 'error', + autoDismiss: true, + }); + } + } finally { + setIsTesting(false); + initialLoad.current = true; + } + }, + [addToast] + ); + + useEffect(() => { + if (radarr) { + testConnection({ + apiKey: radarr.apiKey, + hostname: radarr.hostname, + port: radarr.port, + baseUrl: radarr.baseUrl, + useSsl: radarr.useSsl, + }); + } + }, [radarr, testConnection]); + + return ( + + { + try { + const profileName = testResponse.profiles.find( + (profile) => profile.id === Number(values.activeProfileId) + )?.name; + + const submission = { + name: values.name, + hostname: values.hostname, + port: values.port, + apiKey: values.apiKey, + useSsl: values.ssl, + baseUrl: values.baseUrl, + activeProfileId: values.activeProfileId, + activeProfileName: profileName, + activeDirectory: values.rootFolder, + is4k: values.is4k, + minimumAvailability: 'In Cinema', + isDefault: values.isDefault, + }; + if (!radarr) { + await axios.post('/api/v1/settings/radarr', submission); + } else { + await axios.put( + `/api/v1/settings/radarr/${radarr.id}`, + submission + ); + } + + onSave(); + } catch (e) { + // set error here + } + }} + > + {({ + errors, + touched, + values, + handleSubmit, + setFieldValue, + isSubmitting, + }) => { + return ( + { + if (values.apiKey && values.hostname && values.port) { + testConnection({ + apiKey: values.apiKey, + baseUrl: values.baseUrl, + hostname: values.hostname, + port: values.port, + useSsl: values.ssl, + }); + } + }} + secondaryDisabled={ + !values.apiKey || !values.hostname || !values.port || isTesting + } + okDisabled={!isValidated || isSubmitting || isTesting} + onOk={() => handleSubmit()} + title={ + !radarr ? 'Create New Radarr Instance' : 'Edit Radarr Instance' + } + > +
+
+ +
+ +
+
+
+ +
+
+ ) => { + setIsValidated(false); + setFieldValue('name', e.target.value); + }} + className="flex-1 form-input block w-full min-w-0 rounded-md transition duration-150 ease-in-out sm:text-sm sm:leading-5 bg-cool-gray-700 border border-cool-gray-500" + /> +
+ {errors.name && touched.name && ( +
{errors.name}
+ )} +
+
+
+ +
+
+ ) => { + setIsValidated(false); + setFieldValue('hostname', e.target.value); + }} + className="flex-1 form-input block w-full min-w-0 rounded-md transition duration-150 ease-in-out sm:text-sm sm:leading-5 bg-cool-gray-700 border border-cool-gray-500" + /> +
+ {errors.hostname && touched.hostname && ( +
{errors.hostname}
+ )} +
+
+
+ +
+ ) => { + setIsValidated(false); + setFieldValue('port', e.target.value); + }} + className="rounded-md shadow-sm form-input block w-24 transition duration-150 ease-in-out sm:text-sm sm:leading-5 bg-cool-gray-700 border border-cool-gray-500" + /> + {errors.port && touched.port && ( +
{errors.port}
+ )} +
+
+
+ +
+ { + setIsValidated(false); + setFieldValue('ssl', !values.ssl); + }} + className="form-checkbox h-6 w-6 text-indigo-600 transition duration-150 ease-in-out" + /> +
+
+
+ +
+
+ ) => { + setIsValidated(false); + setFieldValue('apiKey', e.target.value); + }} + className="flex-1 form-input block w-full min-w-0 rounded-md transition duration-150 ease-in-out sm:text-sm sm:leading-5 bg-cool-gray-700 border border-cool-gray-500" + /> +
+ {errors.apiKey && touched.apiKey && ( +
{errors.apiKey}
+ )} +
+
+
+ +
+
+ ) => { + setIsValidated(false); + setFieldValue('baseUrl', e.target.value); + }} + className="flex-1 form-input block w-full min-w-0 rounded-md transition duration-150 ease-in-out sm:text-sm sm:leading-5 bg-cool-gray-700 border border-cool-gray-500" + /> +
+ {errors.baseUrl && touched.baseUrl && ( +
{errors.baseUrl}
+ )} +
+
+
+ +
+
+ + {testResponse.profiles.length > 0 && + testResponse.profiles.map((profile) => ( + + ))} + +
+ {errors.baseUrl && touched.baseUrl && ( +
{errors.baseUrl}
+ )} +
+
+
+ +
+
+ + {testResponse.rootFolders.length > 0 && + testResponse.rootFolders.map((folder) => ( + + ))} + +
+ {errors.baseUrl && touched.baseUrl && ( +
{errors.baseUrl}
+ )} +
+
+
+ +
+ +
+
+
+
+ ); + }} +
+
+ ); +}; + +export default RadarrModal; diff --git a/src/components/Settings/SettingsPlex.tsx b/src/components/Settings/SettingsPlex.tsx index 01d2c21f3..c39c0e3b6 100644 --- a/src/components/Settings/SettingsPlex.tsx +++ b/src/components/Settings/SettingsPlex.tsx @@ -285,10 +285,10 @@ const SettingsPlex: React.FC = () => {

-
+
{dataSync?.running && (
void; + onDelete: () => void; +} + +const ServerInstance: React.FC = ({ + name, + address, + profileName, + isDefault4K = false, + isDefault = false, + isSSL = false, + isSonarr = false, + onEdit, + onDelete, +}) => { + return ( +
  • +
    +
    +
    +

    + {name} +

    + {isDefault && Default} + {isDefault4K && Default 4K} + {isSSL && SSL} +
    +

    + Address + {address} +

    +

    + Active Profile {profileName} +

    +
    + +
    +
    +
    +
    + +
    +
    + +
    +
    +
    +
  • + ); +}; + +const SettingsServices: React.FC = () => { + const { + data: radarrData, + error: radarrError, + revalidate: revalidateRadarr, + } = useSWR('/api/v1/settings/radarr'); + const { data: sonarrData, error: sonarrError } = useSWR( + '/api/v1/settings/sonarr' + ); + const [editRadarrModal, setEditRadarrModal] = useState<{ + open: boolean; + radarr: RadarrSettings | null; + }>({ + open: false, + radarr: null, + }); + + return ( + <> +
    +

    + Radarr Settings +

    +

    + Configure your Radarr connection below. You can have multiple Radarr + configurations but only two can be active as defaults at any time (one + for standard HD and one for 4K). Administrations can override a titles + connection to use in the manage title screen. +

    +
    + {editRadarrModal.open && ( + setEditRadarrModal({ open: false, radarr: null })} + onSave={() => { + revalidateRadarr(); + setEditRadarrModal({ open: false, radarr: null }); + }} + /> + )} +
    + {!radarrData && !radarrError && } + {radarrData && !radarrError && ( +
      + {radarrData.map((radarr) => ( + setEditRadarrModal({ open: true, radarr })} + onDelete={() => console.log('delete clicked')} + /> + ))} +
    • +
      + +
      +
    • +
    + )} +
    +
    +

    + Sonarr Settings +

    +

    + Configure your Sonarr connection below. You can have multiple Sonarr + configurations but only two can be active as defaults at any time (one + for standard HD and one for 4K). Administrations can override a titles + connection to use in the manage title screen. +

    +
    +
    + {!sonarrData && !sonarrError && } + {sonarrData && !sonarrError && ( +
      + {sonarrData.map((sonarr) => ( + console.log('nada')} + onDelete={() => console.log('delete clicked')} + /> + ))} +
    • +
      + +
      +
    • +
    + )} +
    + + ); +}; + +export default SettingsServices; diff --git a/src/pages/settings/services.tsx b/src/pages/settings/services.tsx new file mode 100644 index 000000000..d7cafe8a1 --- /dev/null +++ b/src/pages/settings/services.tsx @@ -0,0 +1,14 @@ +import React from 'react'; +import type { NextPage } from 'next'; +import SettingsLayout from '../../components/Settings/SettingsLayout'; +import SettingsServices from '../../components/Settings/SettingsServices'; + +const ServicesSettingsPage: NextPage = () => { + return ( + + + + ); +}; + +export default ServicesSettingsPage; diff --git a/yarn.lock b/yarn.lock index bb4fa5fc9..7df5cde58 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1059,6 +1059,13 @@ dependencies: regenerator-runtime "^0.13.4" +"@babel/runtime@^7.10.5": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.12.1.tgz#b4116a6b6711d010b2dad3b7b6e43bf1b9954740" + integrity sha512-J5AIf3vPj3UwXaAzb5j1xM4WAQDX3EMgemF8rjCP3SoW09LfRKAXQKt6CoVYl230P6iWdRcBbnLDDdnqWxZSCA== + dependencies: + regenerator-runtime "^0.13.4" + "@babel/template@^7.10.4", "@babel/template@^7.7.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.10.4.tgz#3251996c4200ebc71d1a8fc405fba940f36ba278" @@ -1889,6 +1896,11 @@ resolved "https://registry.yarnpkg.com/@types/yamljs/-/yamljs-0.2.31.tgz#b1a620b115c96db7b3bfdf0cf54aee0c57139245" integrity sha512-QcJ5ZczaXAqbVD3o8mw/mEBhRvO5UAdTtbvgwL/OgoWubvNBh6/MxLBAigtcgIFaq3shon9m3POIxQaLQt4fxQ== +"@types/yup@^0.29.9": + version "0.29.9" + resolved "https://registry.yarnpkg.com/@types/yup/-/yup-0.29.9.tgz#e2015187ae5739fd3b791b3b7ab9094f2aa5a474" + integrity sha512-ZtjjlrHuHTYctHDz3c8XgInjj0v+Hahe32N/4cDa2banibf9w6aAgxwx0jZtBjKKzmGIU4NXhofEsBW1BbqrNg== + "@typescript-eslint/eslint-plugin@^4.0.0": version "4.0.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.0.0.tgz#99349a501447fed91de18346705c0c65cf603bee" @@ -5064,6 +5076,11 @@ flush-write-stream@^1.0.0: inherits "^2.0.3" readable-stream "^2.3.6" +fn-name@~3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/fn-name/-/fn-name-3.0.0.tgz#0596707f635929634d791f452309ab41558e3c5c" + integrity sha512-eNMNr5exLoavuAMhIUVsOKF79SWd/zG104ef6sxBTSw+cZc6BXdQXDvYcGvp0VbxVVSp1XDUNoz7mg1xMtSznA== + fn.name@1.x.x: version "1.1.0" resolved "https://registry.yarnpkg.com/fn.name/-/fn.name-1.1.0.tgz#26cad8017967aea8731bc42961d04a3d5988accc" @@ -6456,7 +6473,7 @@ locate-path@^6.0.0: dependencies: p-locate "^5.0.0" -lodash-es@^4.17.14: +lodash-es@^4.17.11, lodash-es@^4.17.14: version "4.17.15" resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.15.tgz#21bd96839354412f23d7a10340e5eac6ee455d78" integrity sha512-rlrc3yU3+JNOpZ9zj5pQtxnx2THmvRykwL4Xlxoa8I9lHBlVbbyPhgyPMioxVZ4NqyxaVVtaJnzsyOidQIhyyQ== @@ -8309,6 +8326,11 @@ prop-types@15.7.2, prop-types@^15.5.8, prop-types@^15.6.2, prop-types@^15.7.2: object-assign "^4.1.1" react-is "^16.8.1" +property-expr@^2.0.2: + version "2.0.4" + resolved "https://registry.yarnpkg.com/property-expr/-/property-expr-2.0.4.tgz#37b925478e58965031bb612ec5b3260f8241e910" + integrity sha512-sFPkHQjVKheDNnPvotjQmm3KD3uk1fWKUN7CrpdbwmUx3CrG3QiM8QpTSimvig5vTXmTvjz7+TDvXOI9+4rkcg== + proxy-addr@~2.0.5: version "2.0.6" resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.6.tgz#fdc2336505447d3f2f2c638ed272caf614bbb2bf" @@ -9761,6 +9783,11 @@ swr@^0.3.5: dependencies: dequal "2.0.2" +synchronous-promise@^2.0.13: + version "2.0.15" + resolved "https://registry.yarnpkg.com/synchronous-promise/-/synchronous-promise-2.0.15.tgz#07ca1822b9de0001f5ff73595f3d08c4f720eb8e" + integrity sha512-k8uzYIkIVwmT+TcglpdN50pS2y1BDcUnBPK9iJeGu0Pl1lOI8pD6wtzgw91Pjpe+RxtTncw32tLxs/R0yNL2Mg== + table@^5.2.3: version "5.4.6" resolved "https://registry.yarnpkg.com/table/-/table-5.4.6.tgz#1292d19500ce3f86053b05f0e8e7e4a3bb21079e" @@ -9993,6 +10020,11 @@ toidentifier@1.0.0: resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553" integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw== +toposort@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/toposort/-/toposort-2.0.2.tgz#ae21768175d1559d48bef35420b2f4962f09c330" + integrity sha1-riF2gXXRVZ1IvvNUILL0li8JwzA= + touch@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/touch/-/touch-3.1.0.tgz#fe365f5f75ec9ed4e56825e0bb76d24ab74af83b" @@ -10771,3 +10803,16 @@ yn@3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== + +yup@^0.29.3: + version "0.29.3" + resolved "https://registry.yarnpkg.com/yup/-/yup-0.29.3.tgz#69a30fd3f1c19f5d9e31b1cf1c2b851ce8045fea" + integrity sha512-RNUGiZ/sQ37CkhzKFoedkeMfJM0vNQyaz+wRZJzxdKE7VfDeVKH8bb4rr7XhRLbHJz5hSjoDNwMEIaKhuMZ8gQ== + dependencies: + "@babel/runtime" "^7.10.5" + fn-name "~3.0.0" + lodash "^4.17.15" + lodash-es "^4.17.11" + property-expr "^2.0.2" + synchronous-promise "^2.0.13" + toposort "^2.0.2"