mirror of
https://github.com/sct/overseerr.git
synced 2025-09-17 17:24:35 +02:00
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
This commit is contained in:
1
.github/workflows/ci.yml
vendored
1
.github/workflows/ci.yml
vendored
@@ -37,4 +37,5 @@ jobs:
|
|||||||
username: ${{ secrets.DOCKER_USERNAME }}
|
username: ${{ secrets.DOCKER_USERNAME }}
|
||||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||||
repository: sctx/overseerr
|
repository: sctx/overseerr
|
||||||
|
build_args: COMMIT_TAG=${{ github.sha }}
|
||||||
tags: develop
|
tags: develop
|
||||||
|
@@ -12,6 +12,9 @@ RUN yarn cache clean
|
|||||||
|
|
||||||
FROM node:12.18-alpine
|
FROM node:12.18-alpine
|
||||||
|
|
||||||
|
ARG COMMIT_TAG
|
||||||
|
ENV COMMIT_TAG=${COMMIT_TAG}
|
||||||
|
|
||||||
COPY . /app
|
COPY . /app
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
|
@@ -1478,6 +1478,29 @@ paths:
|
|||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
$ref: '#/components/schemas/DiscordSettings'
|
$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:
|
/auth/me:
|
||||||
get:
|
get:
|
||||||
summary: Returns the currently logged in user
|
summary: Returns the currently logged in user
|
||||||
|
5
server/interfaces/api/settingsInterfaces.ts
Normal file
5
server/interfaces/api/settingsInterfaces.ts
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
export interface SettingsAboutResponse {
|
||||||
|
version: string;
|
||||||
|
totalRequests: number;
|
||||||
|
totalMediaItems: number;
|
||||||
|
}
|
@@ -17,6 +17,9 @@ import { scheduledJobs } from '../job/schedule';
|
|||||||
import { Permission } from '../lib/permissions';
|
import { Permission } from '../lib/permissions';
|
||||||
import { isAuthenticated } from '../middleware/auth';
|
import { isAuthenticated } from '../middleware/auth';
|
||||||
import { merge } from 'lodash';
|
import { merge } from 'lodash';
|
||||||
|
import { version } from '../../package.json';
|
||||||
|
import Media from '../entity/Media';
|
||||||
|
import { MediaRequest } from '../entity/MediaRequest';
|
||||||
|
|
||||||
const settingsRoutes = Router();
|
const settingsRoutes = Router();
|
||||||
|
|
||||||
@@ -431,4 +434,23 @@ settingsRoutes.post('/notifications/email', (req, res) => {
|
|||||||
res.status(200).json(settings.notifications.agents.email);
|
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;
|
export default settingsRoutes;
|
||||||
|
40
src/components/Common/List/index.tsx
Normal file
40
src/components/Common/List/index.tsx
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { withProperties } from '../../../utils/typeHelpers';
|
||||||
|
|
||||||
|
interface ListItemProps {
|
||||||
|
title: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ListItem: React.FC<ListItemProps> = ({ title, children }) => {
|
||||||
|
return (
|
||||||
|
<div className="py-4 sm:py-5 sm:grid sm:grid-cols-3 sm:gap-4">
|
||||||
|
<dt className="text-sm font-medium text-gray-200">{title}</dt>
|
||||||
|
<dd className="mt-1 flex text-sm text-gray-400 sm:mt-0 sm:col-span-2">
|
||||||
|
<span className="flex-grow">{children}</span>
|
||||||
|
</dd>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
interface ListProps {
|
||||||
|
title: string;
|
||||||
|
subTitle?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const List: React.FC<ListProps> = ({ title, subTitle, children }) => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div>
|
||||||
|
<h3 className="text-lg leading-6 font-medium text-gray-100">{title}</h3>
|
||||||
|
{subTitle && (
|
||||||
|
<p className="mt-1 max-w-2xl text-sm text-gray-300">{subTitle}</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="mt-5 border-t border-gray-800">
|
||||||
|
<dl className="divide-y divide-gray-800">{children}</dl>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default withProperties(List, { Item: ListItem });
|
67
src/components/Settings/SettingsAbout/index.tsx
Normal file
67
src/components/Settings/SettingsAbout/index.tsx
Normal file
@@ -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<SettingsAboutResponse>(
|
||||||
|
'/api/v1/settings/about'
|
||||||
|
);
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
return <Error statusCode={500} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!data && !error) {
|
||||||
|
return <LoadingSpinner />;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!data) {
|
||||||
|
return <LoadingSpinner />;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className="mb-8">
|
||||||
|
<List title="Overseerr Information">
|
||||||
|
<List.Item title="Version">{data.version}</List.Item>
|
||||||
|
<List.Item title="Total Media">
|
||||||
|
<FormattedNumber value={data.totalMediaItems} />
|
||||||
|
</List.Item>
|
||||||
|
<List.Item title="Total Requests">
|
||||||
|
<FormattedNumber value={data.totalRequests} />
|
||||||
|
</List.Item>
|
||||||
|
</List>
|
||||||
|
</div>
|
||||||
|
<div className="mb-8">
|
||||||
|
<List title="Getting Support">
|
||||||
|
<List.Item title="GitHub Discussions">
|
||||||
|
<a
|
||||||
|
href="https://github.com/sct/overseerr/discussions"
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer"
|
||||||
|
className="text-indigo-500 hover:underline"
|
||||||
|
>
|
||||||
|
https://github.com/sct/overseerr/discussions
|
||||||
|
</a>
|
||||||
|
</List.Item>
|
||||||
|
<List.Item title="Discord">
|
||||||
|
<a
|
||||||
|
href="https://discord.gg/PkCWJSeCk7"
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer"
|
||||||
|
className="text-indigo-500 hover:underline"
|
||||||
|
>
|
||||||
|
Click here to join our Discord server.
|
||||||
|
</a>
|
||||||
|
</List.Item>
|
||||||
|
</List>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SettingsAbout;
|
17
src/pages/settings/about.tsx
Normal file
17
src/pages/settings/about.tsx
Normal file
@@ -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 (
|
||||||
|
<SettingsLayout>
|
||||||
|
<SettingsAbout />
|
||||||
|
</SettingsLayout>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SettingsAboutPage;
|
Reference in New Issue
Block a user