mirror of
https://github.com/sct/overseerr.git
synced 2025-09-17 17:24:35 +02:00
fix(api): Use POST instead of GET for API endpoints that mutate state (#877)
This commit is contained in:
@@ -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
|
||||
|
@@ -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({
|
||||
|
@@ -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':
|
||||
|
@@ -489,7 +489,7 @@ requestRoutes.post<{
|
||||
}
|
||||
);
|
||||
|
||||
requestRoutes.get<{
|
||||
requestRoutes.post<{
|
||||
requestId: string;
|
||||
status: 'pending' | 'approve' | 'decline';
|
||||
}>(
|
||||
|
@@ -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) => {
|
||||
|
@@ -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 (
|
||||
<div className="ml-3 relative">
|
||||
<div className="relative ml-3">
|
||||
<div>
|
||||
<button
|
||||
className="max-w-xs flex items-center text-sm rounded-full focus:outline-none focus:ring"
|
||||
className="flex items-center max-w-xs text-sm rounded-full focus:outline-none focus:ring"
|
||||
id="user-menu"
|
||||
aria-label="User menu"
|
||||
aria-haspopup="true"
|
||||
onClick={() => setDropdownOpen(true)}
|
||||
>
|
||||
<img className="h-8 w-8 rounded-full" src={user?.avatar} alt="" />
|
||||
<img className="w-8 h-8 rounded-full" src={user?.avatar} alt="" />
|
||||
</button>
|
||||
</div>
|
||||
<Transition
|
||||
@@ -46,18 +46,18 @@ const UserDropdown: React.FC = () => {
|
||||
leaveTo="transform opacity-0 scale-95"
|
||||
>
|
||||
<div
|
||||
className="origin-top-right absolute right-0 mt-2 w-48 rounded-md shadow-lg"
|
||||
className="absolute right-0 w-48 mt-2 origin-top-right rounded-md shadow-lg"
|
||||
ref={dropdownRef}
|
||||
>
|
||||
<div
|
||||
className="py-1 rounded-md bg-gray-700 ring-1 ring-black ring-opacity-5"
|
||||
className="py-1 bg-gray-700 rounded-md ring-1 ring-black ring-opacity-5"
|
||||
role="menu"
|
||||
aria-orientation="vertical"
|
||||
aria-labelledby="user-menu"
|
||||
>
|
||||
<a
|
||||
href="#"
|
||||
className="block px-4 py-2 text-sm text-gray-200 hover:bg-gray-600 transition ease-in-out duration-150"
|
||||
className="block px-4 py-2 text-sm text-gray-200 transition duration-150 ease-in-out hover:bg-gray-600"
|
||||
role="menuitem"
|
||||
onClick={() => logout()}
|
||||
>
|
||||
|
@@ -123,10 +123,8 @@ const MovieDetails: React.FC<MovieDetailsProps> = ({ movie }) => {
|
||||
};
|
||||
|
||||
const markAvailable = async (is4k = false) => {
|
||||
await axios.get(`/api/v1/media/${data?.mediaInfo?.id}/available`, {
|
||||
params: {
|
||||
is4k,
|
||||
},
|
||||
await axios.post(`/api/v1/media/${data?.mediaInfo?.id}/available`, {
|
||||
is4k,
|
||||
});
|
||||
revalidate();
|
||||
};
|
||||
|
@@ -30,7 +30,7 @@ const RequestBlock: React.FC<RequestBlockProps> = ({ request, onUpdate }) => {
|
||||
|
||||
const updateRequest = async (type: 'approve' | 'decline'): Promise<void> => {
|
||||
setIsUpdating(true);
|
||||
await axios.get(`/api/v1/request/${request.id}/${type}`);
|
||||
await axios.post(`/api/v1/request/${request.id}/${type}`);
|
||||
|
||||
if (onUpdate) {
|
||||
onUpdate();
|
||||
|
@@ -83,7 +83,7 @@ const RequestButton: React.FC<RequestButtonProps> = ({
|
||||
request: MediaRequest,
|
||||
type: 'approve' | 'decline'
|
||||
) => {
|
||||
const response = await axios.get(`/api/v1/request/${request.id}/${type}`);
|
||||
const response = await axios.post(`/api/v1/request/${request.id}/${type}`);
|
||||
|
||||
if (response) {
|
||||
onUpdate();
|
||||
@@ -100,7 +100,7 @@ const RequestButton: React.FC<RequestButtonProps> = ({
|
||||
|
||||
await Promise.all(
|
||||
requests.map(async (request) => {
|
||||
return axios.get(`/api/v1/request/${request.id}/${type}`);
|
||||
return axios.post(`/api/v1/request/${request.id}/${type}`);
|
||||
})
|
||||
);
|
||||
|
||||
|
@@ -62,7 +62,7 @@ const RequestCard: React.FC<RequestCardProps> = ({ request }) => {
|
||||
});
|
||||
|
||||
const modifyRequest = async (type: 'approve' | 'decline') => {
|
||||
const response = await axios.get(`/api/v1/request/${request.id}/${type}`);
|
||||
const response = await axios.post(`/api/v1/request/${request.id}/${type}`);
|
||||
|
||||
if (response) {
|
||||
revalidate();
|
||||
|
@@ -70,7 +70,7 @@ const RequestItem: React.FC<RequestItemProps> = ({
|
||||
const [isRetrying, setRetrying] = useState(false);
|
||||
|
||||
const modifyRequest = async (type: 'approve' | 'decline') => {
|
||||
const response = await axios.get(`/api/v1/request/${request.id}/${type}`);
|
||||
const response = await axios.post(`/api/v1/request/${request.id}/${type}`);
|
||||
|
||||
if (response) {
|
||||
revalidate();
|
||||
|
@@ -63,7 +63,7 @@ const SettingsJobs: React.FC = () => {
|
||||
}
|
||||
|
||||
const runJob = async (job: Job) => {
|
||||
await axios.get(`/api/v1/settings/jobs/${job.id}/run`);
|
||||
await axios.post(`/api/v1/settings/jobs/${job.id}/run`);
|
||||
addToast(
|
||||
intl.formatMessage(messages.jobstarted, {
|
||||
jobname: job.name,
|
||||
@@ -77,7 +77,7 @@ const SettingsJobs: React.FC = () => {
|
||||
};
|
||||
|
||||
const cancelJob = async (job: Job) => {
|
||||
await axios.get(`/api/v1/settings/jobs/${job.id}/cancel`);
|
||||
await axios.post(`/api/v1/settings/jobs/${job.id}/cancel`);
|
||||
addToast(intl.formatMessage(messages.jobcancelled, { jobname: job.name }), {
|
||||
appearance: 'error',
|
||||
autoDismiss: true,
|
||||
@@ -86,7 +86,7 @@ const SettingsJobs: React.FC = () => {
|
||||
};
|
||||
|
||||
const flushCache = async (cache: CacheItem) => {
|
||||
await axios.get(`/api/v1/settings/cache/${cache.id}/flush`);
|
||||
await axios.post(`/api/v1/settings/cache/${cache.id}/flush`);
|
||||
addToast(
|
||||
intl.formatMessage(messages.cacheflushed, { cachename: cache.name }),
|
||||
{
|
||||
|
@@ -70,7 +70,7 @@ const SettingsMain: React.FC = () => {
|
||||
|
||||
const regenerate = async () => {
|
||||
try {
|
||||
await axios.get('/api/v1/settings/main/regenerate');
|
||||
await axios.post('/api/v1/settings/main/regenerate');
|
||||
|
||||
revalidate();
|
||||
addToast(intl.formatMessage(messages.toastApiKeySuccess), {
|
||||
|
@@ -212,19 +212,15 @@ const SettingsPlex: React.FC<SettingsPlexProps> = ({ onComplete }) => {
|
||||
};
|
||||
|
||||
const startScan = async () => {
|
||||
await axios.get('/api/v1/settings/plex/sync', {
|
||||
params: {
|
||||
start: true,
|
||||
},
|
||||
await axios.post('/api/v1/settings/plex/sync', {
|
||||
start: true,
|
||||
});
|
||||
revalidateSync();
|
||||
};
|
||||
|
||||
const cancelScan = async () => {
|
||||
await axios.get('/api/v1/settings/plex/sync', {
|
||||
params: {
|
||||
cancel: true,
|
||||
},
|
||||
await axios.post('/api/v1/settings/plex/sync', {
|
||||
cancel: true,
|
||||
});
|
||||
revalidateSync();
|
||||
};
|
||||
|
@@ -35,7 +35,7 @@ const Setup: React.FC = () => {
|
||||
|
||||
const finishSetup = async () => {
|
||||
setIsUpdating(false);
|
||||
const response = await axios.get<{ initialized: boolean }>(
|
||||
const response = await axios.post<{ initialized: boolean }>(
|
||||
'/api/v1/settings/initialize'
|
||||
);
|
||||
|
||||
|
@@ -127,10 +127,8 @@ const TvDetails: React.FC<TvDetailsProps> = ({ tv }) => {
|
||||
};
|
||||
|
||||
const markAvailable = async (is4k = false) => {
|
||||
await axios.get(`/api/v1/media/${data?.mediaInfo?.id}/available`, {
|
||||
params: {
|
||||
is4k,
|
||||
},
|
||||
await axios.post(`/api/v1/media/${data?.mediaInfo?.id}/available`, {
|
||||
is4k,
|
||||
});
|
||||
revalidate();
|
||||
};
|
||||
|
Reference in New Issue
Block a user