From 320432657e6ccf4d255238098e03590f28267bdb Mon Sep 17 00:00:00 2001 From: sct Date: Tue, 3 Nov 2020 09:44:54 +0000 Subject: [PATCH] feat: sonarr edit/delete modal --- overseerr-api.yml | 44 ++ server/api/radarr.ts | 2 + server/entity/MediaRequest.ts | 1 + server/routes/settings.ts | 29 ++ src/components/Settings/RadarrModal.tsx | 7 +- src/components/Settings/SettingsServices.tsx | 80 ++- src/components/Settings/SonarrModal.tsx | 487 +++++++++++++++++++ 7 files changed, 626 insertions(+), 24 deletions(-) create mode 100644 src/components/Settings/SonarrModal.tsx diff --git a/overseerr-api.yml b/overseerr-api.yml index 43e0518de..c27b7fe0a 100644 --- a/overseerr-api.yml +++ b/overseerr-api.yml @@ -1049,6 +1049,50 @@ paths: application/json: schema: $ref: '#/components/schemas/SonarrSettings' + /settings/sonarr/test: + post: + summary: Test Sonarr configuration + description: Test if the provided Sonarr 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: 8989 + apiKey: + type: string + example: yourapikey + useSsl: + type: boolean + example: false + baseUrl: + type: string + required: + - hostname + - port + - apiKey + - useSsl + responses: + '200': + description: Succesfully connected to Sonarr instance + content: + application/json: + schema: + type: object + properties: + profiles: + type: array + items: + $ref: '#/components/schemas/ServiceProfile' /settings/sonarr/{sonarrId}: put: summary: Update existing sonarr instance diff --git a/server/api/radarr.ts b/server/api/radarr.ts index dbebe6f39..e75e7c521 100644 --- a/server/api/radarr.ts +++ b/server/api/radarr.ts @@ -3,6 +3,7 @@ import Axios, { AxiosInstance } from 'axios'; interface RadarrMovieOptions { title: string; qualityProfileId: number; + minimumAvailability: string; profileId: number; year: number; rootFolderPath: string; @@ -83,6 +84,7 @@ class RadarrAPI { qualityProfileId: options.qualityProfileId, profileId: options.profileId, titleSlug: options.tmdbId.toString(), + minimumAvailability: options.minimumAvailability, tmdbId: options.tmdbId, year: options.year, rootFolderPath: options.rootFolderPath, diff --git a/server/entity/MediaRequest.ts b/server/entity/MediaRequest.ts index a90dbb66f..036bd0eb9 100644 --- a/server/entity/MediaRequest.ts +++ b/server/entity/MediaRequest.ts @@ -155,6 +155,7 @@ export class MediaRequest { profileId: radarrSettings.activeProfileId, qualityProfileId: radarrSettings.activeProfileId, rootFolderPath: radarrSettings.activeDirectory, + minimumAvailability: radarrSettings.minimumAvailability, title: movie.title, tmdbId: movie.id, year: Number(movie.release_date.slice(0, 4)), diff --git a/server/routes/settings.ts b/server/routes/settings.ts index 2c56b3c5a..523e29f7e 100644 --- a/server/routes/settings.ts +++ b/server/routes/settings.ts @@ -254,6 +254,35 @@ settingsRoutes.post('/sonarr', (req, res) => { return res.status(201).json(newSonarr); }); +settingsRoutes.post('/sonarr/test', async (req, res, next) => { + try { + const sonarr = new SonarrAPI({ + apiKey: req.body.apiKey, + url: `${req.body.useSsl ? 'https' : 'http'}://${req.body.hostname}:${ + req.body.port + }${req.body.baseUrl ?? ''}/api`, + }); + + const profiles = await sonarr.getProfiles(); + const folders = await sonarr.getRootFolders(); + + return res.status(200).json({ + profiles, + rootFolders: folders.map((folder) => ({ + id: folder.id, + path: folder.path, + })), + }); + } catch (e) { + logger.error('Failed to test Sonarr', { + label: 'Sonarr', + message: e.message, + }); + + next({ status: 500, message: 'Failed to connect to Sonarr' }); + } +}); + settingsRoutes.put<{ id: string }>('/sonarr/:id', (req, res) => { const settings = getSettings(); diff --git a/src/components/Settings/RadarrModal.tsx b/src/components/Settings/RadarrModal.tsx index b52db3df6..025b88ebc 100644 --- a/src/components/Settings/RadarrModal.tsx +++ b/src/components/Settings/RadarrModal.tsx @@ -240,7 +240,7 @@ const RadarrModal: React.FC = ({ id="name" name="name" type="input" - placeholder="127.0.0.1" + placeholder="A Radarr Server" onChange={(e: React.ChangeEvent) => { setIsValidated(false); setFieldValue('name', e.target.value); @@ -462,11 +462,6 @@ const RadarrModal: React.FC = ({ - {errors.rootFolder && touched.rootFolder && ( -
- {errors.rootFolder} -
- )}
diff --git a/src/components/Settings/SettingsServices.tsx b/src/components/Settings/SettingsServices.tsx index ddaf39377..58e6e3fd1 100644 --- a/src/components/Settings/SettingsServices.tsx +++ b/src/components/Settings/SettingsServices.tsx @@ -12,6 +12,7 @@ import RadarrModal from './RadarrModal'; import Modal from '../Common/Modal'; import Transition from '../Transition'; import axios from 'axios'; +import SonarrModal from './SonarrModal'; interface ServerInstanceProps { name: string; @@ -112,9 +113,11 @@ const SettingsServices: React.FC = () => { error: radarrError, revalidate: revalidateRadarr, } = useSWR('/api/v1/settings/radarr'); - const { data: sonarrData, error: sonarrError } = useSWR( - '/api/v1/settings/sonarr' - ); + const { + data: sonarrData, + error: sonarrError, + revalidate: revalidateSonarr, + } = useSWR('/api/v1/settings/sonarr'); const [editRadarrModal, setEditRadarrModal] = useState<{ open: boolean; radarr: RadarrSettings | null; @@ -122,18 +125,30 @@ const SettingsServices: React.FC = () => { open: false, radarr: null, }); - const [deleteRadarrModal, setDeleteRadarrModal] = useState<{ + const [editSonarrModal, setEditSonarrModal] = useState<{ open: boolean; - radarrId: number | null; + sonarr: SonarrSettings | null; }>({ open: false, - radarrId: null, + sonarr: null, + }); + const [deleteServerModal, setDeleteServerModal] = useState<{ + open: boolean; + type: 'radarr' | 'sonarr'; + serverId: number | null; + }>({ + open: false, + type: 'radarr', + serverId: null, }); - const deleteServer = async (radarrId: number) => { - await axios.delete(`/api/v1/settings/radarr/${radarrId}`); - setDeleteRadarrModal({ open: false, radarrId: null }); + const deleteServer = async () => { + await axios.delete( + `/api/v1/settings/${deleteServerModal.type}/${deleteServerModal.serverId}` + ); + setDeleteServerModal({ open: false, serverId: null, type: 'radarr' }); revalidateRadarr(); + revalidateSonarr(); }; return ( @@ -159,8 +174,18 @@ const SettingsServices: React.FC = () => { }} /> )} + {editSonarrModal.open && ( + setEditSonarrModal({ open: false, sonarr: null })} + onSave={() => { + revalidateSonarr(); + setEditSonarrModal({ open: false, sonarr: null }); + }} + /> + )} { deleteServer(deleteRadarrModal.radarrId ?? 0)} - onCancel={() => setDeleteRadarrModal({ open: false, radarrId: null })} + onOk={() => deleteServer()} + onCancel={() => + setDeleteServerModal({ + open: false, + serverId: null, + type: 'radarr', + }) + } title="Delete Server" > - Are you sure you want to delete this Radarr server? + Are you sure you want to delete this server?
@@ -193,7 +224,11 @@ const SettingsServices: React.FC = () => { isDefault4K={radarr.is4k && radarr.isDefault} onEdit={() => setEditRadarrModal({ open: true, radarr })} onDelete={() => - setDeleteRadarrModal({ open: true, radarrId: radarr.id }) + setDeleteServerModal({ + open: true, + serverId: radarr.id, + type: 'radarr', + }) } /> ))} @@ -244,10 +279,19 @@ const SettingsServices: React.FC = () => { key={`sonarr-config-${sonarr.id}`} name={sonarr.name} address={sonarr.hostname} - profileName={sonarr.activeProfileId.toString()} + profileName={sonarr.activeProfileName} isSSL={sonarr.useSsl} - onEdit={() => console.log('nada')} - onDelete={() => console.log('delete clicked')} + isSonarr + isDefault4K={sonarr.isDefault && sonarr.is4k} + isDefault={sonarr.isDefault && !sonarr.is4k} + onEdit={() => setEditSonarrModal({ open: true, sonarr })} + onDelete={() => + setDeleteServerModal({ + open: true, + serverId: sonarr.id, + type: 'sonarr', + }) + } /> ))}
  • @@ -255,7 +299,7 @@ const SettingsServices: React.FC = () => {