From 34136ef6173ae3a85251bec1eae54966a027207e Mon Sep 17 00:00:00 2001 From: salty Date: Tue, 15 Dec 2020 02:57:01 +0100 Subject: [PATCH 1/9] build(dockerfile): Expose port 3000. (#278) Allows for automated nginx reverse proxy setups to identify the port used in the container without having to bind the port to the host/localhost. --- Dockerfile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Dockerfile b/Dockerfile index e5198a4bc..d157d429b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -21,3 +21,5 @@ COPY --from=BUILD_IMAGE /app/.next ./.next COPY --from=BUILD_IMAGE /app/node_modules ./node_modules CMD yarn start + +EXPOSE 3000 From 33b54c08b52ae77742187b5bcc9697fcf45503ba Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Tue, 15 Dec 2020 11:05:19 +0900 Subject: [PATCH 2/9] docs: add saltydk as a contributor (#286) [skip ci] * docs: update README.md [skip ci] * docs: update .all-contributorsrc [skip ci] Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com> --- .all-contributorsrc | 9 +++++++++ README.md | 3 ++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/.all-contributorsrc b/.all-contributorsrc index 56e321b3b..fac1d1f01 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -88,6 +88,15 @@ "contributions": [ "code" ] + }, + { + "login": "saltydk", + "name": "salty", + "avatar_url": "https://avatars1.githubusercontent.com/u/6587950?v=4", + "profile": "https://github.com/saltydk", + "contributions": [ + "infra" + ] } ], "badgeTemplate": "\"All-orange.svg\"/>", diff --git a/README.md b/README.md index 4a19cfafc..544b2cd68 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ Language grade: JavaScript GitHub -All Contributors +All Contributors

@@ -104,6 +104,7 @@ Our [Code of Conduct](https://github.com/sct/overseerr/blob/develop/CODE_OF_COND
jvennik

🌍
darknessgp

💻 +
salty

🚇 From f998873fc5669a547901f2733c9c785d744d27ca Mon Sep 17 00:00:00 2001 From: Alex Zoitos Date: Mon, 14 Dec 2020 21:11:48 -0500 Subject: [PATCH 3/9] fix(services): improve logging for when Radarr movie already exists (#285) re #260 --- server/api/radarr.ts | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/server/api/radarr.ts b/server/api/radarr.ts index fe22e72c9..4797ef5d7 100644 --- a/server/api/radarr.ts +++ b/server/api/radarr.ts @@ -76,11 +76,9 @@ class RadarrAPI { } }; - public addMovie = async ( - options: RadarrMovieOptions - ): Promise => { + public addMovie = async (options: RadarrMovieOptions): Promise => { try { - const response = await this.axios.post(`/movie`, { + await this.axios.post(`/movie`, { title: options.title, qualityProfileId: options.qualityProfileId, profileId: options.profileId, @@ -94,15 +92,15 @@ class RadarrAPI { searchForMovie: options.searchNow, }, }); - - return response.data; } catch (e) { - logger.error('Something went wrong adding a movie to Radarr', { - label: 'Radarr', - message: e.message, - options, - }); - throw new Error(`[Radarr] Failed to add movie: ${e.message}`); + logger.error( + 'Failed to add movie to Radarr. This might happen if the movie already exists, in which case you can safely ignore this error.', + { + label: 'Radarr', + errorMessage: e.message, + options, + } + ); } }; From 3ba09d07eb0367c41603cd55e7ff41c66fb641c4 Mon Sep 17 00:00:00 2001 From: sct Date: Tue, 15 Dec 2020 03:22:54 +0000 Subject: [PATCH 4/9] fix: add support for ssl when connecting to plex fixes #275 --- server/api/plexapi.ts | 1 + server/lib/settings.ts | 2 + server/types/plex-api.d.ts | 1 + src/components/Settings/SettingsPlex.tsx | 267 ++++++++++++++--------- src/i18n/locale/en.json | 4 +- 5 files changed, 165 insertions(+), 110 deletions(-) diff --git a/server/api/plexapi.ts b/server/api/plexapi.ts index a3abf0526..c8e123710 100644 --- a/server/api/plexapi.ts +++ b/server/api/plexapi.ts @@ -62,6 +62,7 @@ class PlexAPI { this.plexClient = new NodePlexAPI({ hostname: settings.plex.ip, port: settings.plex.port, + https: settings.plex.useSsl, token: plexToken, authenticator: { authenticate: ( diff --git a/server/lib/settings.ts b/server/lib/settings.ts index 1f33f3bee..f618615cb 100644 --- a/server/lib/settings.ts +++ b/server/lib/settings.ts @@ -14,6 +14,7 @@ export interface PlexSettings { machineId?: string; ip: string; port: number; + useSsl?: boolean; libraries: Library[]; } @@ -109,6 +110,7 @@ class Settings { name: '', ip: '127.0.0.1', port: 32400, + useSsl: false, libraries: [], }, radarr: [], diff --git a/server/types/plex-api.d.ts b/server/types/plex-api.d.ts index fd6db2dd9..9222faafc 100644 --- a/server/types/plex-api.d.ts +++ b/server/types/plex-api.d.ts @@ -4,6 +4,7 @@ declare module 'plex-api' { hostname: string; port: number; token?: string; + https?: boolean; authenticator: { authenticate: ( _plexApi: PlexAPI, diff --git a/src/components/Settings/SettingsPlex.tsx b/src/components/Settings/SettingsPlex.tsx index d9d97c5d8..d4fa052ed 100644 --- a/src/components/Settings/SettingsPlex.tsx +++ b/src/components/Settings/SettingsPlex.tsx @@ -2,21 +2,23 @@ import React, { useState } from 'react'; import LoadingSpinner from '../Common/LoadingSpinner'; import type { PlexSettings } from '../../../server/lib/settings'; import useSWR from 'swr'; -import { useFormik } from 'formik'; +import { Formik, Field } from 'formik'; import Button from '../Common/Button'; import axios from 'axios'; import LibraryItem from './LibraryItem'; import Badge from '../Common/Badge'; import { defineMessages, FormattedMessage, useIntl } from 'react-intl'; +import * as Yup from 'yup'; const messages = defineMessages({ plexsettings: 'Plex Settings', plexsettingsDescription: 'Configure the settings for your Plex server. Overseerr uses your Plex server to scan your library at an interval and see what content is available.', - servername: 'Server Name (Automatically Set)', + servername: 'Server Name (Automatically set after you save)', servernamePlaceholder: 'Plex Server Name', hostname: 'Hostname/IP', port: 'Port', + ssl: 'SSL', save: 'Save Changes', saving: 'Saving...', plexlibraries: 'Plex Libraries', @@ -32,6 +34,8 @@ const messages = defineMessages({ librariesRemaining: 'Libraries Remaining: {count}', startscan: 'Start Scan', cancelscan: 'Cancel Scan', + validationHostnameRequired: 'You must provide a hostname/IP', + validationPortRequired: 'You must provide a port', }); interface Library { @@ -64,33 +68,15 @@ const SettingsPlex: React.FC = ({ onComplete }) => { } ); const [isSyncing, setIsSyncing] = useState(false); - const [isUpdating, setIsUpdating] = useState(false); const [submitError, setSubmitError] = useState(null); - const formik = useFormik({ - initialValues: { - hostname: data?.ip, - port: data?.port, - }, - enableReinitialize: true, - onSubmit: async (values) => { - setSubmitError(null); - setIsUpdating(true); - try { - await axios.post('/api/v1/settings/plex', { - ip: values.hostname, - port: Number(values.port), - } as PlexSettings); - revalidate(); - if (onComplete) { - onComplete(); - } - } catch (e) { - setSubmitError(e.response.data.message); - } finally { - setIsUpdating(false); - } - }, + const PlexSettingsSchema = Yup.object().shape({ + hostname: Yup.string().required( + intl.formatMessage(messages.validationHostnameRequired) + ), + port: Yup.number().required( + intl.formatMessage(messages.validationPortRequired) + ), }); const activeLibraries = @@ -164,91 +150,154 @@ const SettingsPlex: React.FC = ({ onComplete }) => {

-
-
- {submitError && ( -
- {submitError} -
- )} -
- -
-
- + { + setSubmitError(null); + try { + await axios.post('/api/v1/settings/plex', { + ip: values.hostname, + port: Number(values.port), + useSsl: values.useSsl, + } as PlexSettings); + + revalidate(); + if (onComplete) { + onComplete(); + } + } catch (e) { + setSubmitError(e.response.data.message); + } + }} + > + {({ + errors, + touched, + values, + handleSubmit, + setFieldValue, + isSubmitting, + }) => { + return ( + +
+ {submitError && ( +
+ {submitError} +
+ )} +
+ +
+
+ +
+
+
+
+ +
+
+ +
+ {errors.hostname && touched.hostname && ( +
{errors.hostname}
+ )} +
+
+
+ +
+
+ +
+ {errors.port && touched.port && ( +
{errors.port}
+ )} +
+
-
-
-
- -
-
- +
+ +
+ { + setFieldValue('useSsl', !values.useSsl); + }} + className="form-checkbox h-6 w-6 rounded-md text-indigo-600 transition duration-150 ease-in-out" + /> +
-
-
-
- -
-
- +
+
+ + + +
-
-
-
-
-
- - - -
-
- + + ); + }} +

diff --git a/src/i18n/locale/en.json b/src/i18n/locale/en.json index a9d4c7aed..b7530f42e 100644 --- a/src/i18n/locale/en.json +++ b/src/i18n/locale/en.json @@ -210,7 +210,7 @@ "components.Settings.runnow": "Run Now", "components.Settings.save": "Save Changes", "components.Settings.saving": "Saving...", - "components.Settings.servername": "Server Name (Automatically Set)", + "components.Settings.servername": "Server Name (Automatically set after you save)", "components.Settings.servernamePlaceholder": "Plex Server Name", "components.Settings.sonarrSettingsDescription": "Set up your Sonarr connection below. You can have multiple, but only two active as defaults at any time (one for standard HD and one for 4K). Administrators can override which server is used for new requests.", "components.Settings.sonarrsettings": "Sonarr Settings", @@ -218,6 +218,8 @@ "components.Settings.startscan": "Start Scan", "components.Settings.sync": "Sync Plex Libraries", "components.Settings.syncing": "Syncing…", + "components.Settings.validationHostnameRequired": "You must provide a hostname/IP", + "components.Settings.validationPortRequired": "You must provide a port", "components.Setup.configureplex": "Configure Plex", "components.Setup.configureservices": "Configure Services", "components.Setup.continue": "Continue", From b5988f9a5ff274e97f208c2726abe76c22c858ee Mon Sep 17 00:00:00 2001 From: sct Date: Tue, 15 Dec 2020 03:30:13 +0000 Subject: [PATCH 5/9] fix(frontend): add validation for Radarr/Sonarr server name --- src/components/Settings/RadarrModal/index.tsx | 4 ++++ src/components/Settings/SonarrModal/index.tsx | 4 ++++ src/i18n/locale/en.json | 2 ++ 3 files changed, 10 insertions(+) diff --git a/src/components/Settings/RadarrModal/index.tsx b/src/components/Settings/RadarrModal/index.tsx index cce4c37b6..f7d80081f 100644 --- a/src/components/Settings/RadarrModal/index.tsx +++ b/src/components/Settings/RadarrModal/index.tsx @@ -11,6 +11,7 @@ import { defineMessages, useIntl } from 'react-intl'; const messages = defineMessages({ createradarr: 'Create New Radarr Server', editradarr: 'Edit Radarr Server', + validationNameRequired: 'You must provide a server name', validationHostnameRequired: 'You must provide a hostname/IP', validationPortRequired: 'You must provide a port', validationApiKeyRequired: 'You must provide an API key', @@ -74,6 +75,9 @@ const RadarrModal: React.FC = ({ rootFolders: [], }); const RadarrSettingsSchema = Yup.object().shape({ + name: Yup.string().required( + intl.formatMessage(messages.validationNameRequired) + ), hostname: Yup.string().required( intl.formatMessage(messages.validationHostnameRequired) ), diff --git a/src/components/Settings/SonarrModal/index.tsx b/src/components/Settings/SonarrModal/index.tsx index 248006dd6..a98c64ed0 100644 --- a/src/components/Settings/SonarrModal/index.tsx +++ b/src/components/Settings/SonarrModal/index.tsx @@ -11,6 +11,7 @@ import { useIntl, defineMessages } from 'react-intl'; const messages = defineMessages({ createsonarr: 'Create New Sonarr Server', editsonarr: 'Edit Sonarr Server', + validationNameRequired: 'You must provide a server name', validationHostnameRequired: 'You must provide a hostname/IP', validationPortRequired: 'You must provide a port', validationApiKeyRequired: 'You must provide an API key', @@ -73,6 +74,9 @@ const SonarrModal: React.FC = ({ rootFolders: [], }); const SonarrSettingsSchema = Yup.object().shape({ + name: Yup.string().required( + intl.formatMessage(messages.validationNameRequired) + ), hostname: Yup.string().required( intl.formatMessage(messages.validationHostnameRequired) ), diff --git a/src/i18n/locale/en.json b/src/i18n/locale/en.json index b7530f42e..af814e4b8 100644 --- a/src/i18n/locale/en.json +++ b/src/i18n/locale/en.json @@ -135,6 +135,7 @@ "components.Settings.RadarrModal.toastRadarrTestSuccess": "Radarr connection established!", "components.Settings.RadarrModal.validationApiKeyRequired": "You must provide an API key", "components.Settings.RadarrModal.validationHostnameRequired": "You must provide a hostname/IP", + "components.Settings.RadarrModal.validationNameRequired": "You must provide a server name", "components.Settings.RadarrModal.validationPortRequired": "You must provide a port", "components.Settings.RadarrModal.validationProfileRequired": "You must select a profile", "components.Settings.RadarrModal.validationRootFolderRequired": "You must select a root folder", @@ -165,6 +166,7 @@ "components.Settings.SonarrModal.toastRadarrTestSuccess": "Sonarr connection established!", "components.Settings.SonarrModal.validationApiKeyRequired": "You must provide an API key", "components.Settings.SonarrModal.validationHostnameRequired": "You must provide a hostname/IP", + "components.Settings.SonarrModal.validationNameRequired": "You must provide a server name", "components.Settings.SonarrModal.validationPortRequired": "You must provide a port", "components.Settings.SonarrModal.validationProfileRequired": "You must select a profile", "components.Settings.SonarrModal.validationRootFolderRequired": "You must select a root folder", From ff618956b5d9cf933d867ea979b612c3d8a6f30b Mon Sep 17 00:00:00 2001 From: sct Date: Tue, 15 Dec 2020 05:15:45 +0000 Subject: [PATCH 6/9] fix(frontend): only show alpha notice to admins --- src/components/Layout/index.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/components/Layout/index.tsx b/src/components/Layout/index.tsx index a581b6674..799a32579 100644 --- a/src/components/Layout/index.tsx +++ b/src/components/Layout/index.tsx @@ -6,6 +6,7 @@ import Notifications from './Notifications'; import LanguagePicker from './LanguagePicker'; import { useRouter } from 'next/router'; import { defineMessages, FormattedMessage } from 'react-intl'; +import { Permission, useUser } from '../../hooks/useUser'; const messages = defineMessages({ alphawarning: @@ -14,6 +15,7 @@ const messages = defineMessages({ const Layout: React.FC = ({ children }) => { const [isSidebarOpen, setSidebarOpen] = useState(false); + const { hasPermission } = useUser(); const router = useRouter(); return ( @@ -57,7 +59,7 @@ const Layout: React.FC = ({ children }) => { >
- {router.pathname === '/' && ( + {router.pathname === '/' && hasPermission(Permission.ADMIN) && (
@@ -85,7 +87,7 @@ const Layout: React.FC = ({ children }) => { target="_blank" rel="noreferrer" > - Github → + GitHub →

From 3f2a04c881bf06b73a952181fa463af84454b0dd Mon Sep 17 00:00:00 2001 From: sct Date: Tue, 15 Dec 2020 07:27:30 +0000 Subject: [PATCH 7/9] feat: about page initial version Adding something to the about page for now, including the app version to better know what versions of the app people are running --- .github/workflows/ci.yml | 1 + Dockerfile | 3 + overseerr-api.yml | 23 +++++++ server/interfaces/api/settingsInterfaces.ts | 5 ++ server/routes/settings.ts | 22 ++++++ src/components/Common/List/index.tsx | 40 +++++++++++ .../Settings/SettingsAbout/index.tsx | 67 +++++++++++++++++++ src/pages/settings/about.tsx | 17 +++++ 8 files changed, 178 insertions(+) create mode 100644 server/interfaces/api/settingsInterfaces.ts create mode 100644 src/components/Common/List/index.tsx create mode 100644 src/components/Settings/SettingsAbout/index.tsx create mode 100644 src/pages/settings/about.tsx diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5629d4533..8d401771f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -37,4 +37,5 @@ jobs: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} repository: sctx/overseerr + build_args: COMMIT_TAG=${{ github.sha }} tags: develop diff --git a/Dockerfile b/Dockerfile index d157d429b..35fb08b1f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -12,6 +12,9 @@ RUN yarn cache clean FROM node:12.18-alpine +ARG COMMIT_TAG +ENV COMMIT_TAG=${COMMIT_TAG} + COPY . /app WORKDIR /app diff --git a/overseerr-api.yml b/overseerr-api.yml index 4b92a9eef..07da768af 100644 --- a/overseerr-api.yml +++ b/overseerr-api.yml @@ -1478,6 +1478,29 @@ paths: application/json: schema: $ref: '#/components/schemas/DiscordSettings' + /settings/about: + get: + summary: Return current about stats + description: Returns current server stats in JSON format + tags: + - settings + responses: + '200': + description: Returned about settings + content: + application/json: + schema: + type: object + properties: + version: + type: string + example: '1.0.0' + totalRequests: + type: number + example: 100 + totalMediaItems: + type: number + example: 100 /auth/me: get: summary: Returns the currently logged in user diff --git a/server/interfaces/api/settingsInterfaces.ts b/server/interfaces/api/settingsInterfaces.ts new file mode 100644 index 000000000..bc6dcf024 --- /dev/null +++ b/server/interfaces/api/settingsInterfaces.ts @@ -0,0 +1,5 @@ +export interface SettingsAboutResponse { + version: string; + totalRequests: number; + totalMediaItems: number; +} diff --git a/server/routes/settings.ts b/server/routes/settings.ts index 422510f2e..93a69b40c 100644 --- a/server/routes/settings.ts +++ b/server/routes/settings.ts @@ -17,6 +17,9 @@ import { scheduledJobs } from '../job/schedule'; import { Permission } from '../lib/permissions'; import { isAuthenticated } from '../middleware/auth'; import { merge } from 'lodash'; +import { version } from '../../package.json'; +import Media from '../entity/Media'; +import { MediaRequest } from '../entity/MediaRequest'; const settingsRoutes = Router(); @@ -431,4 +434,23 @@ settingsRoutes.post('/notifications/email', (req, res) => { res.status(200).json(settings.notifications.agents.email); }); +settingsRoutes.get('/about', async (req, res) => { + const mediaRepository = getRepository(Media); + const mediaRequestRepository = getRepository(MediaRequest); + + const totalMediaItems = await mediaRepository.count(); + const totalRequests = await mediaRequestRepository.count(); + + let finalVersion = version; + + if (version === '0.1.0') { + finalVersion = `develop-${process.env.COMMIT_TAG ?? 'local'}`; + } + return res.status(200).json({ + version: finalVersion, + totalMediaItems, + totalRequests, + }); +}); + export default settingsRoutes; diff --git a/src/components/Common/List/index.tsx b/src/components/Common/List/index.tsx new file mode 100644 index 000000000..462f6ce84 --- /dev/null +++ b/src/components/Common/List/index.tsx @@ -0,0 +1,40 @@ +import React from 'react'; +import { withProperties } from '../../../utils/typeHelpers'; + +interface ListItemProps { + title: string; +} + +const ListItem: React.FC = ({ title, children }) => { + return ( +
+
{title}
+
+ {children} +
+
+ ); +}; + +interface ListProps { + title: string; + subTitle?: string; +} + +const List: React.FC = ({ title, subTitle, children }) => { + return ( + <> +
+

{title}

+ {subTitle && ( +

{subTitle}

+ )} +
+
+
{children}
+
+ + ); +}; + +export default withProperties(List, { Item: ListItem }); diff --git a/src/components/Settings/SettingsAbout/index.tsx b/src/components/Settings/SettingsAbout/index.tsx new file mode 100644 index 000000000..b11d64add --- /dev/null +++ b/src/components/Settings/SettingsAbout/index.tsx @@ -0,0 +1,67 @@ +import React from 'react'; +import useSWR from 'swr'; +import Error from '../../../pages/_error'; +import List from '../../Common/List'; +import LoadingSpinner from '../../Common/LoadingSpinner'; +import { SettingsAboutResponse } from '../../../../server/interfaces/api/settingsInterfaces'; +import { FormattedNumber } from 'react-intl'; + +const SettingsAbout: React.FC = () => { + const { data, error } = useSWR( + '/api/v1/settings/about' + ); + + if (error) { + return ; + } + + if (!data && !error) { + return ; + } + + if (!data) { + return ; + } + + return ( + <> +
+ + {data.version} + + + + + + + +
+ + + ); +}; + +export default SettingsAbout; diff --git a/src/pages/settings/about.tsx b/src/pages/settings/about.tsx new file mode 100644 index 000000000..442669d9c --- /dev/null +++ b/src/pages/settings/about.tsx @@ -0,0 +1,17 @@ +import { NextPage } from 'next'; +import React from 'react'; +import SettingsAbout from '../../components/Settings/SettingsAbout'; +import SettingsLayout from '../../components/Settings/SettingsLayout'; +import useRouteGuard from '../../hooks/useRouteGuard'; +import { Permission } from '../../hooks/useUser'; + +const SettingsAboutPage: NextPage = () => { + useRouteGuard(Permission.MANAGE_SETTINGS); + return ( + + + + ); +}; + +export default SettingsAboutPage; From b9faa6486b35aa865019aa8af9d307531054bc1d Mon Sep 17 00:00:00 2001 From: sct Date: Tue, 15 Dec 2020 07:56:53 +0000 Subject: [PATCH 8/9] fix(api): require package.json directly so typescript doesnt compile it into dist folder --- server/routes/settings.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/server/routes/settings.ts b/server/routes/settings.ts index 93a69b40c..fee4e5c78 100644 --- a/server/routes/settings.ts +++ b/server/routes/settings.ts @@ -17,7 +17,6 @@ import { scheduledJobs } from '../job/schedule'; import { Permission } from '../lib/permissions'; import { isAuthenticated } from '../middleware/auth'; import { merge } from 'lodash'; -import { version } from '../../package.json'; import Media from '../entity/Media'; import { MediaRequest } from '../entity/MediaRequest'; @@ -441,6 +440,9 @@ settingsRoutes.get('/about', async (req, res) => { const totalMediaItems = await mediaRepository.count(); const totalRequests = await mediaRequestRepository.count(); + // eslint-disable-next-line @typescript-eslint/no-var-requires + const { version } = require('../../package.json'); + let finalVersion = version; if (version === '0.1.0') { From 900827be97845688e4bea72a8c5d9611a3e9d069 Mon Sep 17 00:00:00 2001 From: sct Date: Tue, 15 Dec 2020 08:08:01 +0000 Subject: [PATCH 9/9] feat(lang): add i18n strings for new about page --- .../Settings/SettingsAbout/index.tsx | 29 ++++++++++++++----- src/i18n/locale/en.json | 7 +++++ 2 files changed, 28 insertions(+), 8 deletions(-) diff --git a/src/components/Settings/SettingsAbout/index.tsx b/src/components/Settings/SettingsAbout/index.tsx index b11d64add..7925e1935 100644 --- a/src/components/Settings/SettingsAbout/index.tsx +++ b/src/components/Settings/SettingsAbout/index.tsx @@ -4,9 +4,20 @@ import Error from '../../../pages/_error'; import List from '../../Common/List'; import LoadingSpinner from '../../Common/LoadingSpinner'; import { SettingsAboutResponse } from '../../../../server/interfaces/api/settingsInterfaces'; -import { FormattedNumber } from 'react-intl'; +import { defineMessages, FormattedNumber, useIntl } from 'react-intl'; + +const messages = defineMessages({ + overseerrinformation: 'Overseerr Information', + version: 'Version', + totalmedia: 'Total Media', + totalrequests: 'Total Requests', + gettingsupport: 'Getting Support', + githubdiscussions: 'GitHub Discussions', + clickheretojoindiscord: 'Click here to join our Discord server.', +}); const SettingsAbout: React.FC = () => { + const intl = useIntl(); const { data, error } = useSWR( '/api/v1/settings/about' ); @@ -26,19 +37,21 @@ const SettingsAbout: React.FC = () => { return ( <>
- - {data.version} - + + + {data.version} + + - +
- - + + { rel="noreferrer" className="text-indigo-500 hover:underline" > - Click here to join our Discord server. + {intl.formatMessage(messages.clickheretojoindiscord)} diff --git a/src/i18n/locale/en.json b/src/i18n/locale/en.json index af814e4b8..f598cae48 100644 --- a/src/i18n/locale/en.json +++ b/src/i18n/locale/en.json @@ -139,6 +139,13 @@ "components.Settings.RadarrModal.validationPortRequired": "You must provide a port", "components.Settings.RadarrModal.validationProfileRequired": "You must select a profile", "components.Settings.RadarrModal.validationRootFolderRequired": "You must select a root folder", + "components.Settings.SettingsAbout.clickheretojoindiscord": "Click here to join our Discord server.", + "components.Settings.SettingsAbout.gettingsupport": "Getting Support", + "components.Settings.SettingsAbout.githubdiscussions": "GitHub Discussions", + "components.Settings.SettingsAbout.overseerrinformation": "Overseerr Information", + "components.Settings.SettingsAbout.totalmedia": "Total Media", + "components.Settings.SettingsAbout.totalrequests": "Total Requests", + "components.Settings.SettingsAbout.version": "Version", "components.Settings.SonarrModal.add": "Add Server", "components.Settings.SonarrModal.apiKey": "API Key", "components.Settings.SonarrModal.apiKeyPlaceholder": "Your Sonarr API Key",