From ff0b5ed44132cc5a0cd178035796d042ba735a8d Mon Sep 17 00:00:00 2001 From: TheCatLady <52870424+TheCatLady@users.noreply.github.com> Date: Mon, 8 Feb 2021 20:27:48 -0500 Subject: [PATCH] fix(api): Use POST instead of GET for API endpoints that mutate state (#877) --- overseerr-api.yml | 88 +++++++++++++------ server/routes/auth.ts | 2 +- server/routes/media.ts | 4 +- server/routes/request.ts | 2 +- server/routes/settings/index.ts | 20 +++-- src/components/Layout/UserDropdown/index.tsx | 14 +-- src/components/MovieDetails/index.tsx | 6 +- src/components/RequestBlock/index.tsx | 2 +- src/components/RequestButton/index.tsx | 4 +- src/components/RequestCard/index.tsx | 2 +- .../RequestList/RequestItem/index.tsx | 2 +- .../Settings/SettingsJobsCache/index.tsx | 6 +- src/components/Settings/SettingsMain.tsx | 2 +- src/components/Settings/SettingsPlex.tsx | 12 +-- src/components/Setup/index.tsx | 2 +- src/components/TvDetails/index.tsx | 6 +- 16 files changed, 101 insertions(+), 73 deletions(-) diff --git a/overseerr-api.yml b/overseerr-api.yml index c28079dbc..cf7224484 100644 --- a/overseerr-api.yml +++ b/overseerr-api.yml @@ -1537,7 +1537,7 @@ paths: schema: $ref: '#/components/schemas/MainSettings' /settings/main/regenerate: - get: + post: summary: Get main settings with newly-generated API key description: Returns main settings in a JSON object, using the new API key. tags: @@ -1612,21 +1612,50 @@ paths: $ref: '#/components/schemas/PlexLibrary' /settings/plex/sync: get: + summary: Get status of full Plex library sync + description: Returns sync progress in a JSON array. + tags: + - settings + responses: + '200': + description: Status of Plex sync + content: + application/json: + schema: + type: object + properties: + running: + type: boolean + example: false + progress: + type: number + example: 0 + total: + type: number + example: 100 + currentLibrary: + $ref: '#/components/schemas/PlexLibrary' + libraries: + type: array + items: + $ref: '#/components/schemas/PlexLibrary' + post: summary: Start full Plex library sync description: Runs a full Plex library sync and returns the progress in a JSON array. tags: - settings - parameters: - - in: query - name: cancel - schema: - type: boolean - example: false - - in: query - name: start - schema: - type: boolean - example: false + requestBody: + content: + application/json: + schema: + type: object + properties: + cancel: + type: boolean + example: false + start: + type: boolean + example: false responses: '200': description: Status of Plex sync @@ -1946,7 +1975,7 @@ paths: schema: $ref: '#/components/schemas/PublicSettings' /settings/initialize: - get: + post: summary: Initialize application description: Sets the app as initialized, allowing the user to navigate to pages other than the setup page. tags: @@ -1990,7 +2019,7 @@ paths: type: boolean example: false /settings/jobs/{jobId}/run: - get: + post: summary: Invoke a specific job description: Invokes a specific job to run. Will return the new job status in JSON format. tags: @@ -2025,7 +2054,7 @@ paths: type: boolean example: false /settings/jobs/{jobId}/cancel: - get: + post: summary: Cancel a specific job description: Cancels a specific job. Will return the new job status in JSON format. tags: @@ -2095,7 +2124,7 @@ paths: vsize: type: number /settings/cache/{cacheId}/flush: - get: + post: summary: Flush a specific cache description: Flushes all data from the cache ID provided tags: @@ -2511,7 +2540,7 @@ paths: - email - password /auth/logout: - get: + post: summary: Sign out and clear session cookie description: Completely clear the session cookie and associated values, effectively signing the user out. tags: @@ -3187,10 +3216,10 @@ paths: schema: $ref: '#/components/schemas/MediaRequest' /request/{requestId}/{status}: - get: - summary: Update a requests status + post: + summary: Update a request's status description: | - Updates a requests status to approved or declined. Also returns the request in a JSON object. + Updates a request's status to approved or declined. Also returns the request in a JSON object. Requires the `MANAGE_REQUESTS` permission or `ADMIN`. tags: @@ -3681,9 +3710,9 @@ paths: '204': description: Succesfully removed media item /media/{mediaId}/{status}: - get: + post: summary: Update media status - description: Updates a medias status and returns the media in JSON format + description: Updates a media item's status and returns the media in JSON format tags: - media parameters: @@ -3702,12 +3731,15 @@ paths: schema: type: string enum: [available, partial, processing, pending, unknown] - - in: query - name: is4k - description: 4K Status - example: false - schema: - type: boolean + requestBody: + content: + application/json: + schema: + type: object + properties: + is4k: + type: boolean + example: false responses: '200': description: Returned media diff --git a/server/routes/auth.ts b/server/routes/auth.ts index bb20b9ca0..efa212e49 100644 --- a/server/routes/auth.ts +++ b/server/routes/auth.ts @@ -184,7 +184,7 @@ authRoutes.post('/local', async (req, res, next) => { } }); -authRoutes.get('/logout', (req, res, next) => { +authRoutes.post('/logout', (req, res, next) => { req.session?.destroy((err) => { if (err) { return next({ diff --git a/server/routes/media.ts b/server/routes/media.ts index f6c6a505f..c77f77084 100644 --- a/server/routes/media.ts +++ b/server/routes/media.ts @@ -82,7 +82,7 @@ mediaRoutes.get('/', async (req, res, next) => { } }); -mediaRoutes.get< +mediaRoutes.post< { id: string; status: 'available' | 'partial' | 'processing' | 'pending' | 'unknown'; @@ -102,7 +102,7 @@ mediaRoutes.get< return next({ status: 404, message: 'Media does not exist.' }); } - const is4k = Boolean(req.query.is4k); + const is4k = Boolean(req.body.is4k); switch (req.params.status) { case 'available': diff --git a/server/routes/request.ts b/server/routes/request.ts index 7a23ebff9..a97bac0a3 100644 --- a/server/routes/request.ts +++ b/server/routes/request.ts @@ -489,7 +489,7 @@ requestRoutes.post<{ } ); -requestRoutes.get<{ +requestRoutes.post<{ requestId: string; status: 'pending' | 'approve' | 'decline'; }>( diff --git a/server/routes/settings/index.ts b/server/routes/settings/index.ts index 61dabe217..0099d28cb 100644 --- a/server/routes/settings/index.ts +++ b/server/routes/settings/index.ts @@ -54,7 +54,7 @@ settingsRoutes.post('/main', (req, res) => { return res.status(200).json(settings.main); }); -settingsRoutes.get('/main/regenerate', (req, res, next) => { +settingsRoutes.post('/main/regenerate', (req, res, next) => { const settings = getSettings(); const main = settings.regenerateApiKey(); @@ -210,10 +210,14 @@ settingsRoutes.get('/plex/library', async (req, res) => { return res.status(200).json(settings.plex.libraries); }); -settingsRoutes.get('/plex/sync', (req, res) => { - if (req.query.cancel) { +settingsRoutes.get('/plex/sync', (_req, res) => { + return res.status(200).json(jobPlexFullSync.status()); +}); + +settingsRoutes.post('/plex/sync', (req, res) => { + if (req.body.cancel) { jobPlexFullSync.cancel(); - } else if (req.query.start) { + } else if (req.body.start) { jobPlexFullSync.run(); } return res.status(200).json(jobPlexFullSync.status()); @@ -231,7 +235,7 @@ settingsRoutes.get('/jobs', (_req, res) => { ); }); -settingsRoutes.get<{ jobId: string }>('/jobs/:jobId/run', (req, res, next) => { +settingsRoutes.post<{ jobId: string }>('/jobs/:jobId/run', (req, res, next) => { const scheduledJob = scheduledJobs.find((job) => job.id === req.params.jobId); if (!scheduledJob) { @@ -249,7 +253,7 @@ settingsRoutes.get<{ jobId: string }>('/jobs/:jobId/run', (req, res, next) => { }); }); -settingsRoutes.get<{ jobId: string }>( +settingsRoutes.post<{ jobId: string }>( '/jobs/:jobId/cancel', (req, res, next) => { const scheduledJob = scheduledJobs.find( @@ -286,7 +290,7 @@ settingsRoutes.get('/cache', (req, res) => { ); }); -settingsRoutes.get<{ cacheId: AvailableCacheIds }>( +settingsRoutes.post<{ cacheId: AvailableCacheIds }>( '/cache/:cacheId/flush', (req, res, next) => { const cache = cacheManager.getCache(req.params.cacheId); @@ -300,7 +304,7 @@ settingsRoutes.get<{ cacheId: AvailableCacheIds }>( } ); -settingsRoutes.get( +settingsRoutes.post( '/initialize', isAuthenticated(Permission.ADMIN), (_req, res) => { diff --git a/src/components/Layout/UserDropdown/index.tsx b/src/components/Layout/UserDropdown/index.tsx index 16a40a5af..302574a78 100644 --- a/src/components/Layout/UserDropdown/index.tsx +++ b/src/components/Layout/UserDropdown/index.tsx @@ -16,7 +16,7 @@ const UserDropdown: React.FC = () => { useClickOutside(dropdownRef, () => setDropdownOpen(false)); const logout = async () => { - const response = await axios.get('/api/v1/auth/logout'); + const response = await axios.post('/api/v1/auth/logout'); if (response.data?.status === 'ok') { revalidate(); @@ -24,16 +24,16 @@ const UserDropdown: React.FC = () => { }; return ( -