Merge pull request #540 from ylei-tsubame/rebuild-web

Web UI: patch 520, 401, 518, 514, and additional small issues
main
Digimer 1 year ago committed by GitHub
commit ba4addd653
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 17
      striker-ui-api/README.md
  2. 2
      striker-ui-api/out/index.js
  3. 15
      striker-ui-api/src/lib/consts/ENV.ts
  4. 7
      striker-ui-api/src/lib/request_handlers/auth/login.ts
  5. 2
      striker-ui-api/src/lib/request_handlers/auth/logout.ts
  6. 40
      striker-ui-api/src/middlewares/session.ts
  7. 21
      striker-ui-api/src/routes/static.ts
  8. 26
      striker-ui/components/NetworkInitForm.tsx
  9. 4
      striker-ui/components/PrepareNetworkForm.tsx
  10. 8
      striker-ui/components/StrikerInitForm.tsx
  11. 16
      striker-ui/hooks/useCookieJar.ts
  12. 6
      striker-ui/hooks/useFormUtils.ts
  13. 45
      striker-ui/hooks/useSessionExpiryCheck.ts
  14. 22
      striker-ui/lib/setMapNetwork.tsx
  15. 1
      striker-ui/out/_next/static/Qp2JU8cXYp9Orw8l0IAb0/_buildManifest.js
  16. 0
      striker-ui/out/_next/static/Qp2JU8cXYp9Orw8l0IAb0/_middlewareManifest.js
  17. 0
      striker-ui/out/_next/static/Qp2JU8cXYp9Orw8l0IAb0/_ssgManifest.js
  18. 1
      striker-ui/out/_next/static/XrSz84FzD2mYzU_3k8aL2/_buildManifest.js
  19. 1
      striker-ui/out/_next/static/chunks/248-749f2bec4cb43d28.js
  20. 2
      striker-ui/out/_next/static/chunks/336-24770f9b2621610a.js
  21. 1
      striker-ui/out/_next/static/chunks/48-d4400834d0a31c6e.js
  22. 1
      striker-ui/out/_next/static/chunks/825-07aab1f379d63d3c.js
  23. 1
      striker-ui/out/_next/static/chunks/86-447b52c8195dea3d.js
  24. 1
      striker-ui/out/_next/static/chunks/86-afca85346d822222.js
  25. 1
      striker-ui/out/_next/static/chunks/898-e0785f5528d640fa.js
  26. 2
      striker-ui/out/_next/static/chunks/pages/_app-adb26b7c14183a1f.js
  27. 2
      striker-ui/out/_next/static/chunks/pages/anvil-4cce8c1450ca6ceb.js
  28. 2
      striker-ui/out/_next/static/chunks/pages/config-f22ac92929f0daf0.js
  29. 2
      striker-ui/out/_next/static/chunks/pages/file-manager-53de9163caaf5a86.js
  30. 2
      striker-ui/out/_next/static/chunks/pages/index-e791ea908d3ba943.js
  31. 2
      striker-ui/out/_next/static/chunks/pages/init-ce942046cf0bafb2.js
  32. 2
      striker-ui/out/_next/static/chunks/pages/login-6f7a93d56a339079.js
  33. 2
      striker-ui/out/_next/static/chunks/pages/manage-element-8a411e7d32491cf4.js
  34. 1
      striker-ui/out/_next/static/chunks/pages/server-8faafa80170f67f2.js
  35. 1
      striker-ui/out/_next/static/chunks/pages/server-d4d91dcbacc827c4.js
  36. 2
      striker-ui/out/anvil.html
  37. 2
      striker-ui/out/config.html
  38. 2
      striker-ui/out/file-manager.html
  39. 2
      striker-ui/out/index.html
  40. 2
      striker-ui/out/init.html
  41. 2
      striker-ui/out/login.html
  42. 2
      striker-ui/out/manage-element.html
  43. 2
      striker-ui/out/server.html
  44. 20
      striker-ui/pages/_app.tsx
  45. 10
      striker-ui/types/CookieJar.d.ts
  46. 2
      striker-ui/types/FormUtils.d.ts

@ -1,20 +1,24 @@
# Anvil system striker web interface API
## About NPM projects
In essence, this module and the striker web interface module share the same management workflows:
* All `npm` commands must be executed at the project root or lower level(s).
* To prepare the workspace, run `npm install`.
* To produce a production build, run `npm run build`.
- All `npm` commands must be executed at the project root or lower level(s).
- To prepare the workspace, run `npm install`.
- To produce a production build, run `npm run build`.
One major difference is there's no live development mode in this project.
See the striker we interface's [README](../striker-ui/README.md) for more details.
## Run prerequisites
* This API module is targetted at NodeJS version 10, which is the default on CentOS/RHEL 8.
* All executables/files listed in `src/lib/consts/SERVER_PATHS.ts` and their respective dependencies are required.
- This API module is targetted at NodeJS version 10, which is the default on CentOS/RHEL 8.
- All executables/files listed in `src/lib/consts/SERVER_PATHS.ts` and their respective dependencies are required.
## Build
Run `npm run build` to produce a minified script at `out/index.js`. The output script can be executed with NodeJS assuming all prerequisites are met.
There's no need to remove the old build prior to a new build because the build process always overwrites the one file.
@ -22,12 +26,15 @@ There's no need to remove the old build prior to a new build because the build p
`systemd` expects the build to be placed exactly at `/usr/share/striker-ui-api/index.js` on a striker.
## Logs
At the time of writing, no logging library was added. Logs are either `stdout` or `stderr` without levels. When the API runs as a service, its logs can be viewed with `journalctl --unit striker-ui-api`.
Due to the large amount of logs produced, it's highly recommended to note the time of a test, and specify a time frame with `journalctl --since <date parsable time> --until <date parsable time>` to help with the search.
## Systemd service
The service file of this API module is located in `../units/`. Environment varibles can be set with the `Environment=<variable name>=<value>` directive, i.e., to set the main server's port to `80`, use `Environment=PORT=80`.
## Environment varibles
Variables can be set to affect the API's funtionalities, i.e. listen on a different port. A complete list with explanations is located at `src/lib/consts/ENV.ts`

File diff suppressed because one or more lines are too long

@ -7,6 +7,14 @@ import { resolveGid, resolveUid } from '../shell';
*/
export const COOKIE_PREFIX = process.env.COOKIE_PREFIX ?? 'suiapi';
/**
* The max lifespan of a session cookie in milliseconds.
*
* @default 28800000
*/
export const COOKIE_ORIGINAL_MAX_AGE: number =
Number(process.env.COOKIE_ORIGINAL_MAX_AGE) || 28800000;
/**
* The fallback job progress value when queuing jobs.
*
@ -14,16 +22,15 @@ export const COOKIE_PREFIX = process.env.COOKIE_PREFIX ?? 'suiapi';
*
* @default 0
*/
export const DEFAULT_JOB_PROGRESS: number = Number.parseInt(
process.env.DEFAULT_JOB_PROGRESS ?? '0',
);
export const DEFAULT_JOB_PROGRESS: number =
Number(process.env.DEFAULT_JOB_PROGRESS) || 0;
/**
* Port to use by the express app.
*
* @default 8080
*/
export const PORT = Number.parseInt(process.env.PORT ?? '8080');
export const PORT: number = Number(process.env.PORT) || 8080;
/**
* Process user identifier. Also used to set ownership on the access daemon.

@ -7,14 +7,17 @@ export const login: RequestHandler<unknown, unknown, AuthLoginRequestBody> = (
request,
response,
) => {
const { user } = request;
const { session, user } = request;
if (user) {
const { name: userName } = user;
stdout(`Successfully authenticated user [${userName}]`);
response.cookie(cname('user'), user);
response.cookie(cname('session'), {
expires: session?.cookie?.expires,
user,
});
}
response.status(204).send();

@ -11,7 +11,7 @@ export const logout: RequestHandler = (request, response) => {
return response.status(500).send();
}
response.clearCookie(cname('user'));
response.clearCookie(cname('session'));
response.clearCookie(cname('sid'));
return response.status(204).send();

@ -4,15 +4,13 @@ import expressSession, {
Store as BaseSessionStore,
} from 'express-session';
import { DELETED } from '../lib/consts';
import { COOKIE_ORIGINAL_MAX_AGE, DELETED } from '../lib/consts';
import { getLocalHostUUID, query, timestamp, write } from '../lib/accessModule';
import { cname } from '../lib/cname';
import { getSessionSecret } from '../lib/getSessionSecret';
import { stderr, stdout, stdoutVar, uuid } from '../lib/shell';
const DEFAULT_COOKIE_ORIGINAL_MAX_AGE = 28800000; // 8 hours
export class SessionStore extends BaseSessionStore {
constructor(options = {}) {
super(options);
@ -85,7 +83,7 @@ export class SessionStore extends BaseSessionStore {
const data: SessionData = {
cookie: {
maxAge: cookieMaxAge,
originalMaxAge: DEFAULT_COOKIE_ORIGINAL_MAX_AGE,
originalMaxAge: COOKIE_ORIGINAL_MAX_AGE,
},
passport: { user: userUuid },
};
@ -148,28 +146,32 @@ export class SessionStore extends BaseSessionStore {
): Promise<void> {
stdoutVar({ session }, `Touch session ${sid}: `);
try {
const wcode = await write(
`UPDATE sessions
SET modified_date = '${timestamp()}'
WHERE session_uuid = '${sid}';`,
);
// The intent of updating the session modified date is to avoid expiring the
// session when it's actively used by the user. But since the updates are
// flooding the database's history table, disable it for now.
assert(wcode === 0, `Write exited with code ${wcode}`);
} catch (error) {
stderr(
`Failed to complete DB write in touch session ${sid}; CAUSE: ${error}`,
);
// try {
// const wcode = await write(
// `UPDATE sessions
// SET modified_date = '${timestamp()}'
// WHERE session_uuid = '${sid}';`,
// );
return done?.call(null, error);
}
// assert(wcode === 0, `Write exited with code ${wcode}`);
// } catch (error) {
// stderr(
// `Failed to complete DB write in touch session ${sid}; CAUSE: ${error}`,
// );
// return done?.call(null, error);
// }
return done?.call(null);
}
public static calculateCookieMaxAge(
sessionModifiedDate: string,
cookieOriginalMaxAge: number = DEFAULT_COOKIE_ORIGINAL_MAX_AGE,
cookieOriginalMaxAge: number = COOKIE_ORIGINAL_MAX_AGE,
) {
const sessionModifiedEpoch = Date.parse(sessionModifiedDate);
const sessionDeadlineEpoch = sessionModifiedEpoch + cookieOriginalMaxAge;
@ -185,7 +187,7 @@ export default (async () =>
expressSession({
cookie: {
httpOnly: true,
maxAge: DEFAULT_COOKIE_ORIGINAL_MAX_AGE,
maxAge: COOKIE_ORIGINAL_MAX_AGE,
secure: false,
},
genid: ({ originalUrl }) => {

@ -47,6 +47,10 @@ router.use((...args) => {
const { path: p } = rq;
const target = '/init';
// Prevent browsers from caching the initialize page to enable redirect
// after the init restart.
rs.setHeader('Cache-Control', 'must-revalidate, no-store');
if (p.startsWith(target)) return nx();
return rs.redirect(target);
@ -63,6 +67,23 @@ router.use((...args) => {
return rs.redirect(rt ? `${target}?rt=${rt}` : target);
},
failReturnTo: !path.startsWith('/login'),
succeed: (rq, rs, nx) => {
const {
path: p,
query: { re: reinit, rt = '/' },
} = rq;
// Redirect to home or the given return-to path when the user is already
// authenticated.
if (p.startsWith('/login')) return rs.redirect(String(rt));
// Redirect to home when the user tries to access the init page after
// 1) the system is already initialized, and
// 2) the user is already authenticated.
if (p.startsWith('/init') && !reinit) return rs.redirect('/');
return nx();
},
}),
})(...args);
}, express.static(htmlDir, { extensions: ['html'] }));

@ -37,12 +37,10 @@ import { BLUE, GREY } from '../lib/consts/DEFAULT_THEME';
import NETWORK_TYPES from '../lib/consts/NETWORK_TYPES';
import { REP_IPV4, REP_IPV4_CSV } from '../lib/consts/REG_EXP_PATTERNS';
import api from '../lib/api';
import BriefNetworkInterface from './BriefNetworkInterface';
import Decorator from './Decorator';
import DropArea from './DropArea';
import FlexBox from './FlexBox';
import handleAPIError from '../lib/handleAPIError';
import IconButton from './IconButton';
import InputWithRef, { InputForwardedRefContent } from './InputWithRef';
import { Message } from './MessageBox';
@ -51,6 +49,7 @@ import OutlinedInputWithLabel from './OutlinedInputWithLabel';
import { InnerPanel, InnerPanelHeader } from './Panels';
import periodicFetch from '../lib/fetchers/periodicFetch';
import SelectWithLabel from './SelectWithLabel';
import setMapNetwork from '../lib/setMapNetwork';
import Spinner from './Spinner';
import { createTestInputFunction, testNotBlank } from '../lib/test_input';
import { BodyText, MonoText, SmallText } from './Text';
@ -873,20 +872,9 @@ const NetworkInitForm = forwardRef<
[networkInputs],
);
const setMapNetwork = useCallback(
(value: 0 | 1) => {
api.put('/init/set-map-network', { value }).catch((error) => {
const emsg = handleAPIError(error);
emsg.children = (
<>
Failed to {value ? 'enable' : 'disable'} network mapping.{' '}
{emsg.children}
</>
);
setMessage(MSG_ID_API, emsg);
});
const handleSetMapNetworkError = useCallback(
(msg: Message): void => {
setMessage(MSG_ID_API, msg);
},
[setMessage],
);
@ -1383,7 +1371,7 @@ const NetworkInitForm = forwardRef<
useEffect(() => {
// Enable network mapping on component mount.
setMapNetwork(1);
setMapNetwork(1, handleSetMapNetworkError);
if (window) {
window.addEventListener(
@ -1401,9 +1389,9 @@ const NetworkInitForm = forwardRef<
return () => {
// Disable network mapping on component unmount.
setMapNetwork(0);
setMapNetwork(0, handleSetMapNetworkError);
};
}, [setMapNetwork]);
}, [handleSetMapNetworkError]);
useImperativeHandle(
ref,

@ -16,6 +16,7 @@ import NetworkInitForm, {
} from './NetworkInitForm';
import OutlinedInputWithLabel from './OutlinedInputWithLabel';
import { Panel, PanelHeader } from './Panels';
import setMapNetwork from '../lib/setMapNetwork';
import Spinner from './Spinner';
import { buildPeacefulStringTestBatch } from '../lib/test_input';
import { HeaderText } from './Text';
@ -212,6 +213,9 @@ const PrepareNetworkForm: FC<PrepareNetworkFormProps> = ({
<>Failed to prepare network. {parentMsg}</>
),
method: 'put',
onSuccess: () => {
setMapNetwork(0);
},
setMsg: netconfFormRef?.current?.setMessage,
successMsg: `Initiated prepare network on ${hostDetail?.shortHostName}`,
url: `/host/${hostUUID}?handler=subnode-network`,

@ -27,6 +27,7 @@ import NetworkInitForm, {
NetworkInitFormValues,
} from './NetworkInitForm';
import { Panel, PanelHeader } from './Panels';
import setMapNetwork from '../lib/setMapNetwork';
import Spinner from './Spinner';
import { BodyText, HeaderText, InlineMonoText, MonoText } from './Text';
import useProtectedState from '../hooks/useProtectedState';
@ -303,9 +304,14 @@ const StrikerInitForm: FC = () => {
api
.put('/init', requestBody)
.then(() => {
// Stop network mapping only on successful form submission.
setMapNetwork(0);
setIsSubmittingForm(false);
setSubmitMessage({
children: (
children: reconfig ? (
<>Successfully initiated reconfiguration.</>
) : (
<>
Successfully registered the configuration job! You can check
the progress at the top right icon. Once the job completes,

@ -5,7 +5,8 @@ import useIsFirstRender from './useIsFirstRender';
const useCookieJar = (): {
cookieJar: CookieJar;
getCookie: <T>(key: string) => T | undefined;
getSessionUser: () => SessionUser | undefined;
getSession: () => SessionCookie | undefined;
getSessionUser: () => SessionCookieUser | undefined;
} => {
const isFirstRender = useIsFirstRender();
@ -17,11 +18,13 @@ const useCookieJar = (): {
[cookieJar],
);
const getSessionUser = useCallback(
() => getCookie<SessionUser>('user'),
const getSession = useCallback(
() => getCookie<SessionCookie>('session'),
[getCookie],
);
const getSessionUser = useCallback(() => getSession()?.user, [getSession]);
useEffect(() => {
if (isFirstRender) {
const lines = document.cookie.split(/\s*;\s*/);
@ -52,7 +55,12 @@ const useCookieJar = (): {
}
}, [isFirstRender]);
return { cookieJar, getCookie, getSessionUser };
return {
cookieJar,
getCookie,
getSession,
getSessionUser,
};
};
export default useCookieJar;

@ -90,6 +90,8 @@ const useFormUtils = <
getErrorMsg,
msgKey = 'api',
method,
onError,
onSuccess,
setMsg = messageGroupRef?.current?.setMessage,
successMsg,
url,
@ -103,6 +105,8 @@ const useFormUtils = <
children: successMsg,
type: 'info',
});
onSuccess?.call(null);
})
.catch((apiError) => {
const emsg = handleAPIError(apiError);
@ -110,6 +114,8 @@ const useFormUtils = <
emsg.children = getErrorMsg(emsg.children);
setMsg?.call(null, msgKey, emsg);
onError?.call(null);
})
.finally(() => {
setFormSubmitting(false);

@ -0,0 +1,45 @@
import { useEffect, useMemo } from 'react';
import useCookieJar from './useCookieJar';
const useSessionExpiryCheck = (): void => {
const { getSession } = useCookieJar();
// Put session in memo to avoid triggering useEffect multiple times.
const session = useMemo(() => getSession(), [getSession]);
useEffect(() => {
if (!session) return () => null;
const { expires } = session;
const deadline = new Date(expires).getTime();
const nao = Date.now();
const diff = deadline - nao;
const tid = setTimeout(() => {
if (!window) return;
const { location } = window;
const { pathname, search } = location;
if (
/^\/login/.test(pathname) ||
(/^\/init/.test(pathname) && !search.includes('re=1'))
)
return;
location.replace('/login');
}, diff);
if (window) {
window.addEventListener('beforeunload', () => clearTimeout(tid), {
once: true,
});
}
return () => clearTimeout(tid);
}, [session]);
};
export default useSessionExpiryCheck;

@ -0,0 +1,22 @@
import api from './api';
import handleAPIError from './handleAPIError';
const setMapNetwork = (
value: 0 | 1,
handleError?: (msg: Message) => void,
): void => {
api.put('/init/set-map-network', { value }).catch((error) => {
const emsg = handleAPIError(error);
emsg.children = (
<>
Failed to {value ? 'enable' : 'disable'} network mapping.{' '}
{emsg.children}
</>
);
handleError?.call(null, emsg);
});
};
export default setMapNetwork;

@ -0,0 +1 @@
self.__BUILD_MANIFEST=function(s,c,a,e,t,n,i,f,b,d,u,k,h,j,r,g,l,_){return{__rewrites:{beforeFiles:[],afterFiles:[],fallback:[]},"/":[s,a,e,f,b,h,"static/chunks/433-a3be905e7a7d3bfc.js",c,t,n,i,j,r,"static/chunks/pages/index-e791ea908d3ba943.js"],"/_error":["static/chunks/pages/_error-2280fa386d040b66.js"],"/anvil":[s,a,e,f,b,h,c,t,n,i,j,"static/chunks/pages/anvil-4cce8c1450ca6ceb.js"],"/config":[s,a,e,u,"static/chunks/519-4b7761e884c88eb9.js",c,t,n,i,d,k,g,"static/chunks/pages/config-f22ac92929f0daf0.js"],"/file-manager":["static/chunks/29107295-fbcfe2172188e46f.js",s,a,e,f,"static/chunks/176-7308c25ba374961e.js",c,t,i,d,"static/chunks/pages/file-manager-53de9163caaf5a86.js"],"/init":[s,a,f,b,u,l,c,t,n,i,_,"static/chunks/pages/init-ce942046cf0bafb2.js"],"/login":[s,a,e,c,t,n,d,k,"static/chunks/pages/login-6f7a93d56a339079.js"],"/manage-element":[s,a,e,f,b,u,l,"static/chunks/111-2605129c170ed35d.js",c,t,n,i,d,k,_,g,"static/chunks/pages/manage-element-8a411e7d32491cf4.js"],"/server":[s,e,"static/chunks/528-72edc50189f30fa9.js",c,r,"static/chunks/pages/server-8faafa80170f67f2.js"],sortedPages:["/","/_app","/_error","/anvil","/config","/file-manager","/init","/login","/manage-element","/server"]}}("static/chunks/412-ae4bab5809f6a209.js","static/chunks/62-2c80eba24b792af8.js","static/chunks/438-0147a63d98e89439.js","static/chunks/894-e57948de523bcf96.js","static/chunks/195-fa06e61dd4339031.js","static/chunks/987-1ff0d82724b0e58b.js","static/chunks/157-d1418743accab385.js","static/chunks/182-08683bbe95fbb010.js","static/chunks/900-af716a39aed22219.js","static/chunks/48-d4400834d0a31c6e.js","static/chunks/644-c7c6e21c71345aed.js","static/chunks/336-24770f9b2621610a.js","static/chunks/485-77798bccc4308d0e.js","static/chunks/898-e0785f5528d640fa.js","static/chunks/94-8322ed453a3c08f0.js","static/chunks/560-0ed707609765e23a.js","static/chunks/676-6159ce853338cc1f.js","static/chunks/86-afca85346d822222.js"),self.__BUILD_MANIFEST_CB&&self.__BUILD_MANIFEST_CB();

@ -1 +0,0 @@
self.__BUILD_MANIFEST=function(s,a,c,e,t,n,i,f,b,u,k,h,j,d,r,g,l,_){return{__rewrites:{beforeFiles:[],afterFiles:[],fallback:[]},"/":[s,c,e,f,b,j,"static/chunks/433-a3be905e7a7d3bfc.js",a,t,n,i,d,r,"static/chunks/pages/index-0771f2825962ebc3.js"],"/_error":["static/chunks/pages/_error-2280fa386d040b66.js"],"/anvil":[s,c,e,f,b,j,a,t,n,i,d,"static/chunks/pages/anvil-53b02ffa883f4c5a.js"],"/config":[s,c,e,k,"static/chunks/519-4b7761e884c88eb9.js",a,t,n,i,u,h,g,"static/chunks/pages/config-7be24d332b231569.js"],"/file-manager":["static/chunks/29107295-fbcfe2172188e46f.js",s,c,e,f,"static/chunks/176-7308c25ba374961e.js",a,t,i,u,"static/chunks/pages/file-manager-6501dafd856c22ec.js"],"/init":[s,c,f,b,k,l,a,t,n,i,_,"static/chunks/pages/init-7cf62951388d0e3b.js"],"/login":[s,c,e,a,t,n,u,h,"static/chunks/pages/login-0b2f91a926538f7c.js"],"/manage-element":[s,c,e,f,b,k,l,"static/chunks/111-2605129c170ed35d.js",a,t,n,i,u,h,_,g,"static/chunks/pages/manage-element-3ed34f8c3a72590a.js"],"/server":[s,e,"static/chunks/528-72edc50189f30fa9.js",a,r,"static/chunks/pages/server-d4d91dcbacc827c4.js"],sortedPages:["/","/_app","/_error","/anvil","/config","/file-manager","/init","/login","/manage-element","/server"]}}("static/chunks/412-ae4bab5809f6a209.js","static/chunks/62-2c80eba24b792af8.js","static/chunks/438-0147a63d98e89439.js","static/chunks/894-e57948de523bcf96.js","static/chunks/195-fa06e61dd4339031.js","static/chunks/987-1ff0d82724b0e58b.js","static/chunks/157-d1418743accab385.js","static/chunks/182-08683bbe95fbb010.js","static/chunks/900-af716a39aed22219.js","static/chunks/248-749f2bec4cb43d28.js","static/chunks/644-c7c6e21c71345aed.js","static/chunks/336-6e600f08d9387d72.js","static/chunks/485-77798bccc4308d0e.js","static/chunks/825-07aab1f379d63d3c.js","static/chunks/94-8322ed453a3c08f0.js","static/chunks/560-0ed707609765e23a.js","static/chunks/676-6159ce853338cc1f.js","static/chunks/86-447b52c8195dea3d.js"),self.__BUILD_MANIFEST_CB&&self.__BUILD_MANIFEST_CB();

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -6,6 +6,8 @@ import createEmotionCache from '../lib/create_emotion_cache/createEmotionCache';
import theme from '../theme';
import '../styles/globals.css';
import useSessionExpiryCheck from '../hooks/useSessionExpiryCheck';
const clientSideEmotionCache = createEmotionCache();
interface MyAppProps extends AppProps {
@ -17,12 +19,16 @@ const App = ({
Component,
emotionCache = clientSideEmotionCache,
pageProps,
}: MyAppProps): JSX.Element => (
<CacheProvider value={emotionCache}>
<ThemeProvider theme={theme}>
<Component {...pageProps} />
</ThemeProvider>
</CacheProvider>
);
}: MyAppProps): JSX.Element => {
useSessionExpiryCheck();
return (
<CacheProvider value={emotionCache}>
<ThemeProvider theme={theme}>
<Component {...pageProps} />
</ThemeProvider>
</CacheProvider>
);
};
export default App;

@ -1,3 +1,11 @@
type CookieJar = Record<string, unknown>;
type SessionUser = { name: string; uuid: string };
type SessionCookieUser = {
name: string;
uuid: string;
};
type SessionCookie = {
expires: string;
user: SessionCookieUser;
};

@ -27,6 +27,8 @@ type SubmitFormFunction = (args: {
) => import('react').ReactNode;
msgKey?: string;
method: 'delete' | 'post' | 'put';
onError?: () => void;
onSuccess?: () => void;
setMsg?: import('../components/MessageGroup').MessageGroupForwardedRefContent['setMessage'];
successMsg?: import('react').ReactNode;
url: string;

Loading…
Cancel
Save