Skip to content

Commit 86918cb

Browse files
committed
refactor(deprecation): add deprecated route handling for /blacklist endpoint
1 parent c0db0ca commit 86918cb

File tree

3 files changed

+180
-0
lines changed

3 files changed

+180
-0
lines changed

seerr-api.yml

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4638,6 +4638,126 @@ paths:
46384638
responses:
46394639
'204':
46404640
description: Succesfully removed media item
4641+
/blacklist:
4642+
get:
4643+
summary: Returns blocklisted items
4644+
description: |
4645+
**DEPRECATED**: Use `/blocklist` instead. This endpoint will be deprecated soon.
4646+
deprecated: true
4647+
tags:
4648+
- blocklist
4649+
parameters:
4650+
- in: query
4651+
name: take
4652+
schema:
4653+
type: number
4654+
nullable: true
4655+
example: 25
4656+
- in: query
4657+
name: skip
4658+
schema:
4659+
type: number
4660+
nullable: true
4661+
example: 0
4662+
- in: query
4663+
name: search
4664+
schema:
4665+
type: string
4666+
nullable: true
4667+
example: dune
4668+
- in: query
4669+
name: filter
4670+
schema:
4671+
type: string
4672+
enum: [all, manual, blocklistedTags]
4673+
default: manual
4674+
responses:
4675+
'200':
4676+
description: Blocklisted items returned
4677+
content:
4678+
application/json:
4679+
schema:
4680+
type: object
4681+
properties:
4682+
pageInfo:
4683+
$ref: '#/components/schemas/PageInfo'
4684+
results:
4685+
type: array
4686+
items:
4687+
type: object
4688+
properties:
4689+
user:
4690+
$ref: '#/components/schemas/User'
4691+
createdAt:
4692+
type: string
4693+
example: 2024-04-21T01:55:44.000Z
4694+
id:
4695+
type: number
4696+
example: 1
4697+
mediaType:
4698+
type: string
4699+
example: movie
4700+
title:
4701+
type: string
4702+
example: Dune
4703+
tmdbId:
4704+
type: number
4705+
example: 438631
4706+
post:
4707+
summary: Add media to blocklist
4708+
description: |
4709+
**DEPRECATED**: Use `/blocklist` instead. This endpoint will be deprecated soon.
4710+
deprecated: true
4711+
tags:
4712+
- blocklist
4713+
requestBody:
4714+
required: true
4715+
content:
4716+
application/json:
4717+
schema:
4718+
$ref: '#/components/schemas/Blocklist'
4719+
responses:
4720+
'201':
4721+
description: Item succesfully blocklisted
4722+
'412':
4723+
description: Item has already been blocklisted
4724+
/blacklist/{tmdbId}:
4725+
get:
4726+
summary: Get media from blocklist
4727+
description: |
4728+
**DEPRECATED**: Use `/blocklist/{tmdbId}` instead. This endpoint will be deprecated soon.
4729+
deprecated: true
4730+
tags:
4731+
- blocklist
4732+
parameters:
4733+
- in: path
4734+
name: tmdbId
4735+
description: tmdbId ID
4736+
required: true
4737+
example: '1'
4738+
schema:
4739+
type: string
4740+
responses:
4741+
'200':
4742+
description: Blocklist details in JSON
4743+
delete:
4744+
summary: Remove media from blocklist
4745+
description: |
4746+
**DEPRECATED**: Use `/blocklist/{tmdbId}` instead. This endpoint will be deprecated soon.
4747+
deprecated: true
4748+
tags:
4749+
- blocklist
4750+
parameters:
4751+
- in: path
4752+
name: tmdbId
4753+
description: tmdbId ID
4754+
required: true
4755+
example: '1'
4756+
schema:
4757+
type: string
4758+
responses:
4759+
'204':
4760+
description: Succesfully removed media item
46414761
/watchlist:
46424762
post:
46434763
summary: Add media to watchlist

server/middleware/deprecation.ts

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import logger from '@server/logger';
2+
import type { NextFunction, Request, Response } from 'express';
3+
4+
interface DeprecationOptions {
5+
oldPath: string;
6+
newPath: string;
7+
sunsetDate?: string;
8+
documentationUrl?: string;
9+
}
10+
11+
/**
12+
* Mark an API route as deprecated.
13+
* @see https://datatracker.ietf.org/doc/html/rfc8594
14+
*/
15+
export const deprecatedRoute = ({
16+
oldPath,
17+
newPath,
18+
sunsetDate,
19+
documentationUrl,
20+
}: DeprecationOptions) => {
21+
return (req: Request, res: Response, next: NextFunction) => {
22+
logger.warn(
23+
`Deprecated API endpoint accessed: ${oldPath} → use ${newPath} instead`,
24+
{
25+
label: 'API Deprecation',
26+
ip: req.ip,
27+
userAgent: req.get('User-Agent'),
28+
method: req.method,
29+
path: req.originalUrl,
30+
}
31+
);
32+
33+
res.setHeader('Deprecation', 'true');
34+
35+
const links: string[] = [`<${newPath}>; rel="successor-version"`];
36+
if (documentationUrl) {
37+
links.push(`<${documentationUrl}>; rel="deprecation"`);
38+
}
39+
res.setHeader('Link', links.join(', '));
40+
41+
if (sunsetDate) {
42+
res.setHeader('Sunset', new Date(sunsetDate).toUTCString());
43+
}
44+
45+
next();
46+
};
47+
};
48+
49+
export default deprecatedRoute;

server/routes/index.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { Permission } from '@server/lib/permissions';
1212
import { getSettings } from '@server/lib/settings';
1313
import logger from '@server/logger';
1414
import { checkUser, isAuthenticated } from '@server/middleware/auth';
15+
import deprecatedRoute from '@server/middleware/deprecation';
1516
import { mapWatchProviderDetails } from '@server/models/common';
1617
import { mapProductionCompany } from '@server/models/Movie';
1718
import { mapNetwork } from '@server/models/Tv';
@@ -152,6 +153,16 @@ router.use('/discover', isAuthenticated(), discoverRoutes);
152153
router.use('/request', isAuthenticated(), requestRoutes);
153154
router.use('/watchlist', isAuthenticated(), watchlistRoutes);
154155
router.use('/blocklist', isAuthenticated(), blocklistRoutes);
156+
router.use(
157+
'/blacklist',
158+
isAuthenticated(),
159+
deprecatedRoute({
160+
oldPath: '/api/v1/blacklist',
161+
newPath: '/api/v1/blocklist',
162+
sunsetDate: '2025-06-01',
163+
}),
164+
blocklistRoutes
165+
);
155166
router.use('/movie', isAuthenticated(), movieRoutes);
156167
router.use('/tv', isAuthenticated(), tvRoutes);
157168
router.use('/media', isAuthenticated(), mediaRoutes);

0 commit comments

Comments
 (0)