From a99705f6a5674b436ae28cbc558f4ee6e99ac910 Mon Sep 17 00:00:00 2001 From: sct Date: Thu, 12 Nov 2020 09:07:49 +0000 Subject: [PATCH] feat: force setup if app is not initialized --- overseerr-api.yml | 20 +++++++++++ server/routes/index.ts | 11 +++--- server/routes/media.ts | 18 +++++++--- server/routes/settings.ts | 15 +++++++++ src/components/Discover/index.tsx | 2 +- src/components/Setup/index.tsx | 32 ++++++++++++++++-- src/pages/_app.tsx | 56 ++++++++++++++++++++----------- 7 files changed, 121 insertions(+), 33 deletions(-) diff --git a/overseerr-api.yml b/overseerr-api.yml index 2b6843117..03d5675bc 100644 --- a/overseerr-api.yml +++ b/overseerr-api.yml @@ -1160,6 +1160,7 @@ paths: /settings/public: get: summary: Returns public settings + security: [] description: Returns settings that are not protected or sensitive. Mainly used to determine if the app has been configured for the first time. tags: - settings @@ -1170,6 +1171,19 @@ paths: application/json: schema: $ref: '#/components/schemas/PublicSettings' + /settings/initialize: + get: + summary: Set the application as initialized + description: Sets the app as initalized and allows the user to navigate to pages other than the setup page + tags: + - settings + responses: + '200': + description: Public Settings returned + content: + application/json: + schema: + $ref: '#/components/schemas/PublicSettings' /settings/jobs: get: summary: Returns list of scheduled jobs @@ -1887,6 +1901,12 @@ paths: type: string nullable: true enum: [all, available, partial, processing, pending] + - in: query + name: sort + schema: + type: string + enum: [added, modified] + default: added responses: '200': description: Returned media diff --git a/server/routes/index.ts b/server/routes/index.ts index 8c04bf9b7..b57bec524 100644 --- a/server/routes/index.ts +++ b/server/routes/index.ts @@ -16,6 +16,11 @@ const router = Router(); router.use(checkUser); router.use('/user', isAuthenticated(Permission.MANAGE_USERS), user); +router.get('/settings/public', (_req, res) => { + const settings = getSettings(); + + return res.status(200).json(settings.public); +}); router.use( '/settings', isAuthenticated(Permission.MANAGE_SETTINGS), @@ -29,12 +34,6 @@ router.use('/tv', isAuthenticated(), tvRoutes); router.use('/media', isAuthenticated(), mediaRoutes); router.use('/auth', authRoutes); -router.get('/settings/public', (_req, res) => { - const settings = getSettings(); - - return res.status(200).json(settings.public); -}); - router.get('/', (_req, res) => { return res.status(200).json({ api: 'Overseerr API', diff --git a/server/routes/media.ts b/server/routes/media.ts index 733030366..ca83099aa 100644 --- a/server/routes/media.ts +++ b/server/routes/media.ts @@ -1,5 +1,5 @@ import { Router } from 'express'; -import { getRepository, FindOperator } from 'typeorm'; +import { getRepository, FindOperator, FindOneOptions } from 'typeorm'; import Media from '../entity/Media'; import { MediaStatus } from '../constants/media'; @@ -43,11 +43,21 @@ mediaRoutes.get('/', async (req, res, next) => { statusFilter = undefined; } + let sortFilter: FindOneOptions['order'] = { + id: 'DESC', + }; + + switch (req.query.sort) { + case 'modified': + sortFilter = { + updatedAt: 'DESC', + }; + break; + } + try { const [media, mediaCount] = await mediaRepository.findAndCount({ - order: { - id: 'DESC', - }, + order: sortFilter, where: { status: statusFilter, }, diff --git a/server/routes/settings.ts b/server/routes/settings.ts index e1b992ede..3e6ac12ba 100644 --- a/server/routes/settings.ts +++ b/server/routes/settings.ts @@ -13,6 +13,8 @@ import SonarrAPI from '../api/sonarr'; import RadarrAPI from '../api/radarr'; import logger from '../logger'; import { scheduledJobs } from '../job/schedule'; +import { Permission } from '../lib/permissions'; +import { isAuthenticated } from '../middleware/auth'; const settingsRoutes = Router(); @@ -334,4 +336,17 @@ settingsRoutes.get('/jobs', (req, res) => { ); }); +settingsRoutes.get( + '/initialize', + isAuthenticated(Permission.ADMIN), + (req, res) => { + const settings = getSettings(); + + settings.public.initialized = true; + settings.save(); + + return res.status(200).json(settings.public); + } +); + export default settingsRoutes; diff --git a/src/components/Discover/index.tsx b/src/components/Discover/index.tsx index 7434aff70..26e8c27c9 100644 --- a/src/components/Discover/index.tsx +++ b/src/components/Discover/index.tsx @@ -44,7 +44,7 @@ const Discover: React.FC = () => { ); const { data: media, error: mediaError } = useSWR( - '/api/v1/media?filter=available&take=20' + '/api/v1/media?filter=available&take=20&sort=modified' ); const { data: requests, error: requestError } = useSWR( diff --git a/src/components/Setup/index.tsx b/src/components/Setup/index.tsx index b571b529a..c55677ecc 100644 --- a/src/components/Setup/index.tsx +++ b/src/components/Setup/index.tsx @@ -6,12 +6,33 @@ import SettingsPlex from '../Settings/SettingsPlex'; import SettingsServices from '../Settings/SettingsServices'; import LoginWithPlex from './LoginWithPlex'; import SetupSteps from './SetupSteps'; +import axios from 'axios'; +import { defineMessages, FormattedMessage } from 'react-intl'; + +const messages = defineMessages({ + finish: 'Finish Setup', + finishing: 'Finishing...', + continue: 'Continue', +}); const Setup: React.FC = () => { + const [isUpdating, setIsUpdating] = useState(false); const [currentStep, setCurrentStep] = useState(1); const [plexSettingsComplete, setPlexSettingsComplete] = useState(false); const router = useRouter(); + const finishSetup = async () => { + setIsUpdating(false); + const response = await axios.get<{ initialized: boolean }>( + '/api/v1/settings/initialize' + ); + + setIsUpdating(false); + if (response.data.initialized) { + router.push('/'); + } + }; + return (
{ disabled={!plexSettingsComplete} onClick={() => setCurrentStep(3)} > - Continue +
@@ -83,9 +104,14 @@ const Setup: React.FC = () => { diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx index 4893e2b68..8a1289656 100644 --- a/src/pages/_app.tsx +++ b/src/pages/_app.tsx @@ -96,29 +96,47 @@ CoreApp.getInitialProps = async (initialProps) => { let locale = 'en'; if (ctx.res) { - try { - // Attempt to get the user by running a request to the local api - const response = await axios.get( - `http://localhost:${process.env.PORT || 3000}/api/v1/auth/me`, - { headers: ctx.req ? { cookie: ctx.req.headers.cookie } : undefined } - ); - user = response.data; - - if (router.pathname.match(/login/)) { + // Check if app is initialized and redirect if necessary + let initialized = true; + + const response = await axios.get<{ initialized: boolean }>( + `http://localhost:${process.env.PORT || 3000}/api/v1/settings/public` + ); + + initialized = response.data.initialized; + + if (!initialized) { + if (!router.pathname.match(/(setup|login\/plex)/)) { ctx.res.writeHead(307, { - Location: '/', + Location: '/setup', }); ctx.res.end(); } - } catch (e) { - // If there is no user, and ctx.res is set (to check if we are on the server side) - // _AND_ we are not already on the login or setup route, redirect to /login with a 307 - // before anything actually renders - if (!router.pathname.match(/(login|setup)/)) { - ctx.res.writeHead(307, { - Location: '/login', - }); - ctx.res.end(); + } else { + try { + // Attempt to get the user by running a request to the local api + const response = await axios.get( + `http://localhost:${process.env.PORT || 3000}/api/v1/auth/me`, + { headers: ctx.req ? { cookie: ctx.req.headers.cookie } : undefined } + ); + user = response.data; + + if (router.pathname.match(/login/)) { + ctx.res.writeHead(307, { + Location: '/', + }); + ctx.res.end(); + } + } catch (e) { + // If there is no user, and ctx.res is set (to check if we are on the server side) + // _AND_ we are not already on the login or setup route, redirect to /login with a 307 + // before anything actually renders + if (!router.pathname.match(/(login|setup)/)) { + ctx.res.writeHead(307, { + Location: '/login', + }); + ctx.res.end(); + } } }