Skip to content

Commit 75a4264

Browse files
committed
feat: status checker to prompt users to reload their frontend when app version changes
1 parent 1438b08 commit 75a4264

File tree

7 files changed

+103
-11
lines changed

7 files changed

+103
-11
lines changed

next.config.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
module.exports = {
2+
env: {
3+
commitTag: process.env.COMMIT_TAG || 'local',
4+
},
25
webpack(config) {
36
config.module.rules.push({
47
test: /\.svg$/,

overseerr-api.yml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1102,6 +1102,26 @@ components:
11021102
name: X-Api-Key
11031103

11041104
paths:
1105+
/status:
1106+
get:
1107+
summary: Return Overseerr version
1108+
description: Returns the current Overseerr version in JSON format
1109+
security: []
1110+
tags:
1111+
- public
1112+
responses:
1113+
'200':
1114+
description: Returned version
1115+
content:
1116+
application/json:
1117+
schema:
1118+
type: object
1119+
properties:
1120+
version:
1121+
type: string
1122+
example: 1.0.0
1123+
commitTag:
1124+
type: string
11051125
/settings/main:
11061126
get:
11071127
summary: Returns main settings

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@
171171
[
172172
"@semantic-release/exec",
173173
{
174-
"prepareCmd": "docker build -t sctx/overseerr ."
174+
"prepareCmd": "docker build --build-arg COMMIT_TAG=$GITHUB_SHA -t sctx/overseerr ."
175175
}
176176
],
177177
"semantic-release-docker",

server/routes/index.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,19 @@ import tvRoutes from './tv';
1313
import mediaRoutes from './media';
1414
import personRoutes from './person';
1515
import collectionRoutes from './collection';
16+
import { getAppVersion } from '../utils/appVersion';
1617

1718
const router = Router();
1819

1920
router.use(checkUser);
21+
22+
router.get('/status', (req, res) => {
23+
return res.status(200).json({
24+
version: getAppVersion(),
25+
commitTag: process.env.COMMIT_TAG || 'local',
26+
});
27+
});
28+
2029
router.use('/user', isAuthenticated(Permission.MANAGE_USERS), user);
2130
router.get('/settings/public', (_req, res) => {
2231
const settings = getSettings();

src/components/Layout/Sidebar/index.tsx

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -175,11 +175,9 @@ const Sidebar: React.FC<SidebarProps> = ({ open, setClosed }) => {
175175
>
176176
<div className="flex-shrink-0 flex items-center px-4">
177177
<span className="text-xl text-gray-50">
178-
<Link href="/">
179-
<a>
180-
<img src="/logo.png" alt="Overseerr Logo" />
181-
</a>
182-
</Link>
178+
<a href="/">
179+
<img src="/logo.png" alt="Overseerr Logo" />
180+
</a>
183181
</span>
184182
</div>
185183
<nav className="mt-5 px-2 space-y-1">
@@ -239,11 +237,9 @@ const Sidebar: React.FC<SidebarProps> = ({ open, setClosed }) => {
239237
<div className="flex-1 flex flex-col pt-5 pb-4 overflow-y-auto">
240238
<div className="flex items-center flex-shrink-0 px-4">
241239
<span className="text-2xl text-gray-50">
242-
<Link href="/">
243-
<a>
244-
<img src="/logo.png" alt="Overseerr Logo" />
245-
</a>
246-
</Link>
240+
<a href="/">
241+
<img src="/logo.png" alt="Overseerr Logo" />
242+
</a>
247243
</span>
248244
</div>
249245
<nav className="mt-5 flex-1 px-2 bg-gray-800 space-y-1">
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import React from 'react';
2+
import useSWR from 'swr';
3+
import Modal from '../Common/Modal';
4+
import Transition from '../Transition';
5+
6+
const StatusChecker: React.FC = () => {
7+
const { data, error } = useSWR<{ version: string; commitTag: string }>(
8+
'/api/v1/status',
9+
{
10+
refreshInterval: 60 * 1000,
11+
}
12+
);
13+
14+
if (!data && !error) {
15+
return null;
16+
}
17+
18+
if (!data) {
19+
return null;
20+
}
21+
22+
return (
23+
<Transition
24+
enter="opacity-0 transition duration-300"
25+
enterFrom="opacity-0"
26+
enterTo="opacity-100"
27+
leave="opacity-100 transition duration-300"
28+
leaveFrom="opacity-100"
29+
leaveTo="opacity-0"
30+
appear
31+
show={data.commitTag !== process.env.commitTag}
32+
>
33+
<Modal
34+
iconSvg={
35+
<svg
36+
className="w-6 h-6"
37+
fill="none"
38+
stroke="currentColor"
39+
viewBox="0 0 24 24"
40+
xmlns="http://www.w3.org/2000/svg"
41+
>
42+
<path
43+
strokeLinecap="round"
44+
strokeLinejoin="round"
45+
strokeWidth={2}
46+
d="M5 3v4M3 5h4M6 17v4m-2-2h4m5-16l2.286 6.857L21 12l-5.714 2.143L13 21l-2.286-6.857L5 12l5.714-2.143L13 3z"
47+
/>
48+
</svg>
49+
}
50+
title="New Version Available"
51+
onOk={() => location.reload()}
52+
okText="Reload Overseerr"
53+
backgroundClickable={false}
54+
>
55+
An update is now available. Click the button below to reload the
56+
application.
57+
</Modal>
58+
</Transition>
59+
);
60+
};
61+
62+
export default StatusChecker;

src/pages/_app.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { LanguageContext, AvailableLocales } from '../context/LanguageContext';
1313
import Head from 'next/head';
1414
import Toast from '../components/Toast';
1515
import { InteractionProvider } from '../context/InteractionContext';
16+
import StatusChecker from '../components/StatusChacker';
1617

1718
// eslint-disable-next-line @typescript-eslint/no-explicit-any
1819
const loadLocaleData = (locale: string): Promise<any> => {
@@ -104,6 +105,7 @@ const CoreApp: Omit<NextAppComponentType, 'origGetInitialProps'> = ({
104105
<Head>
105106
<title>Overseerr</title>
106107
</Head>
108+
<StatusChecker />
107109
<UserContext initialUser={user}>{component}</UserContext>
108110
</ToastProvider>
109111
</InteractionProvider>

0 commit comments

Comments
 (0)