소스 검색

refactor(get-status): use client-side fetch and API route instead of server action

Nicolas Meienberger 1 년 전
부모
커밋
06bf92daa5

+ 2 - 1
src/app/(dashboard)/settings/components/GeneralActions/GeneralActions.tsx

@@ -20,7 +20,7 @@ export const GeneralActions = (props: Props) => {
   const { version } = props;
   const { version } = props;
 
 
   const [loading, setLoading] = React.useState(false);
   const [loading, setLoading] = React.useState(false);
-  const { setPollStatus } = useSystemStore();
+  const { setPollStatus, setStatus } = useSystemStore();
   const restartDisclosure = useDisclosure();
   const restartDisclosure = useDisclosure();
 
 
   const defaultVersion = '0.0.0';
   const defaultVersion = '0.0.0';
@@ -30,6 +30,7 @@ export const GeneralActions = (props: Props) => {
     onSuccess: (data) => {
     onSuccess: (data) => {
       if (data.success) {
       if (data.success) {
         setPollStatus(true);
         setPollStatus(true);
+        setStatus('RESTARTING');
       } else {
       } else {
         restartDisclosure.close();
         restartDisclosure.close();
         setLoading(false);
         setLoading(false);

+ 0 - 22
src/app/actions/settings/get-status.ts

@@ -1,22 +0,0 @@
-'use server';
-
-import { z } from 'zod';
-import { action } from '@/lib/safe-action';
-import { getConfig } from '@/server/core/TipiConfig';
-import { handleActionError } from '../utils/handle-action-error';
-
-const input = z.object({ currentStatus: z.string() });
-
-/**
- * Fetches the system status and compares it to the current status
- * If the status has changed, it will revalidate the whole app
- */
-export const getStatusAction = action(input, async () => {
-  try {
-    const { status } = getConfig();
-
-    return { success: true, status };
-  } catch (e) {
-    return handleActionError(e);
-  }
-});

+ 9 - 0
src/app/api/get-status/route.ts

@@ -0,0 +1,9 @@
+import { TipiCache } from '@/server/core/TipiCache';
+
+export async function GET() {
+  const cache = new TipiCache('getStatus');
+  const status = (await cache.get('status')) || 'RUNNING';
+  await cache.close();
+
+  return Response.json({ success: true, status });
+}

+ 9 - 17
src/client/components/hoc/StatusProvider/StatusProvider.tsx

@@ -1,39 +1,31 @@
 'use client';
 'use client';
 
 
 import React, { useRef, useEffect } from 'react';
 import React, { useRef, useEffect } from 'react';
-import { useAction } from 'next-safe-action/hook';
-import { useInterval } from 'usehooks-ts';
-import { getStatusAction } from '@/actions/settings/get-status';
-import { useSystemStore } from '@/client/state/systemStore';
+import { SystemStatus, useSystemStore } from '@/client/state/systemStore';
 import { useRouter } from 'next/navigation';
 import { useRouter } from 'next/navigation';
+import useSWR, { Fetcher } from 'swr';
 import { StatusScreen } from '../../StatusScreen';
 import { StatusScreen } from '../../StatusScreen';
 
 
 interface IProps {
 interface IProps {
   children: React.ReactNode;
   children: React.ReactNode;
 }
 }
 
 
+const fetcher: Fetcher<{ status: SystemStatus }> = () => fetch('/api/get-status').then((res) => res.json() as Promise<{ status: SystemStatus }>);
+
 export const StatusProvider: React.FC<IProps> = ({ children }) => {
 export const StatusProvider: React.FC<IProps> = ({ children }) => {
   const { status, setStatus, pollStatus, setPollStatus } = useSystemStore();
   const { status, setStatus, pollStatus, setPollStatus } = useSystemStore();
   const s = useRef(status);
   const s = useRef(status);
 
 
   const router = useRouter();
   const router = useRouter();
 
 
-  const getStatusMutation = useAction(getStatusAction, {
-    onSuccess: (data) => {
-      if (data?.success) {
-        setStatus(data.status);
-      }
+  useSWR('/api/get-status', fetcher, {
+    refreshInterval: pollStatus ? 2000 : 0,
+    isPaused: () => !pollStatus,
+    onSuccess: (res) => {
+      setStatus(res.status);
     },
     },
   });
   });
 
 
-  // Poll status every 5 seconds
-  useInterval(
-    () => {
-      getStatusMutation.execute({ currentStatus: status });
-    },
-    pollStatus ? 2000 : null,
-  );
-
   useEffect(() => {
   useEffect(() => {
     // If previous was not running and current is running, we need to refresh the page
     // If previous was not running and current is running, we need to refresh the page
     if (status === 'RUNNING' && s.current !== 'RUNNING') {
     if (status === 'RUNNING' && s.current !== 'RUNNING') {

+ 12 - 10
src/server/services/system/system.service.ts

@@ -5,7 +5,7 @@ import { EventDispatcher } from '@/server/core/EventDispatcher';
 import { TipiCache } from '@/server/core/TipiCache';
 import { TipiCache } from '@/server/core/TipiCache';
 import { readJsonFile } from '../../common/fs.helpers';
 import { readJsonFile } from '../../common/fs.helpers';
 import { Logger } from '../../core/Logger';
 import { Logger } from '../../core/Logger';
-import * as TipiConfig from '../../core/TipiConfig';
+import { getConfig } from '../../core/TipiConfig';
 
 
 const SYSTEM_STATUS = ['UPDATING', 'RESTARTING', 'RUNNING'] as const;
 const SYSTEM_STATUS = ['UPDATING', 'RESTARTING', 'RUNNING'] as const;
 type SystemStatus = (typeof SYSTEM_STATUS)[keyof typeof SYSTEM_STATUS];
 type SystemStatus = (typeof SYSTEM_STATUS)[keyof typeof SYSTEM_STATUS];
@@ -35,8 +35,7 @@ export class SystemServiceClass {
   public getVersion = async () => {
   public getVersion = async () => {
     const cache = new TipiCache('getVersion');
     const cache = new TipiCache('getVersion');
     try {
     try {
-      const { seePreReleaseVersions } = TipiConfig.getConfig();
-      const currentVersion = TipiConfig.getConfig().version;
+      const { seePreReleaseVersions, version: currentVersion } = getConfig();
 
 
       if (seePreReleaseVersions) {
       if (seePreReleaseVersions) {
         const { data } = await axios.get<{ tag_name: string; body: string }[]>('https://api.github.com/repos/runtipi/runtipi/releases');
         const { data } = await axios.get<{ tag_name: string; body: string }[]>('https://api.github.com/repos/runtipi/runtipi/releases');
@@ -57,10 +56,10 @@ export class SystemServiceClass {
         await cache.set('latestVersionBody', body || '', 60 * 60);
         await cache.set('latestVersionBody', body || '', 60 * 60);
       }
       }
 
 
-      return { current: TipiConfig.getConfig().version, latest: version, body };
+      return { current: getConfig().version, latest: version, body };
     } catch (e) {
     } catch (e) {
       Logger.error(e);
       Logger.error(e);
-      return { current: TipiConfig.getConfig().version, latest: TipiConfig.getConfig().version, body: '' };
+      return { current: getConfig().version, latest: getConfig().version, body: '' };
     } finally {
     } finally {
       await cache.close();
       await cache.close();
     }
     }
@@ -79,15 +78,18 @@ export class SystemServiceClass {
   };
   };
 
 
   public restart = async (): Promise<boolean> => {
   public restart = async (): Promise<boolean> => {
-    if (TipiConfig.getConfig().NODE_ENV === 'development') {
+    if (getConfig().NODE_ENV === 'development') {
       throw new TranslatedError('server-messages.errors.not-allowed-in-dev');
       throw new TranslatedError('server-messages.errors.not-allowed-in-dev');
     }
     }
 
 
-    if (TipiConfig.getConfig().demoMode) {
+    if (getConfig().demoMode) {
       throw new TranslatedError('server-messages.errors.not-allowed-in-demo');
       throw new TranslatedError('server-messages.errors.not-allowed-in-demo');
     }
     }
 
 
-    TipiConfig.setConfig('status', 'RESTARTING');
+    const cache = new TipiCache('restart');
+    await cache.set('status', 'RESTARTING', 360);
+    await cache.close();
+
     const dispatcher = new EventDispatcher('restart');
     const dispatcher = new EventDispatcher('restart');
     dispatcher.dispatchEvent({ type: 'system', command: 'restart' });
     dispatcher.dispatchEvent({ type: 'system', command: 'restart' });
     await dispatcher.close();
     await dispatcher.close();
@@ -95,7 +97,7 @@ export class SystemServiceClass {
     return true;
     return true;
   };
   };
 
 
-  public static status = async (): Promise<{ status: SystemStatus }> => ({
-    status: TipiConfig.getConfig().status,
+  public static status = (): { status: SystemStatus } => ({
+    status: getConfig().status,
   });
   });
 }
 }