nextjs-cms 0.0.1 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +289 -0
- package/dist/api/axios/axiosInstance.d.ts +2 -0
- package/dist/api/axios/axiosInstance.d.ts.map +1 -0
- package/dist/api/axios/axiosInstance.js +8 -0
- package/dist/api/index.d.ts +856 -0
- package/dist/api/index.d.ts.map +1 -0
- package/dist/api/index.js +12 -0
- package/dist/api/lib/serverActions.d.ts +240 -0
- package/dist/api/lib/serverActions.d.ts.map +1 -0
- package/dist/api/lib/serverActions.js +834 -0
- package/dist/api/root.d.ts +829 -0
- package/dist/api/root.d.ts.map +1 -0
- package/dist/api/root.js +30 -0
- package/dist/api/routers/accountSettings.d.ts +61 -0
- package/dist/api/routers/accountSettings.d.ts.map +1 -0
- package/dist/api/routers/accountSettings.js +108 -0
- package/dist/api/routers/admins.d.ts +106 -0
- package/dist/api/routers/admins.d.ts.map +1 -0
- package/dist/api/routers/admins.js +219 -0
- package/dist/api/routers/auth.d.ts +48 -0
- package/dist/api/routers/auth.d.ts.map +1 -0
- package/dist/api/routers/auth.js +25 -0
- package/dist/api/routers/categorySection.d.ts +104 -0
- package/dist/api/routers/categorySection.d.ts.map +1 -0
- package/dist/api/routers/categorySection.js +38 -0
- package/dist/api/routers/cmsSettings.d.ts +49 -0
- package/dist/api/routers/cmsSettings.d.ts.map +1 -0
- package/dist/api/routers/cmsSettings.js +51 -0
- package/dist/api/routers/cpanel.d.ts +84 -0
- package/dist/api/routers/cpanel.d.ts.map +1 -0
- package/dist/api/routers/cpanel.js +216 -0
- package/dist/api/routers/files.d.ts +48 -0
- package/dist/api/routers/files.d.ts.map +1 -0
- package/dist/api/routers/files.js +23 -0
- package/dist/api/routers/gallery.d.ts +36 -0
- package/dist/api/routers/gallery.d.ts.map +1 -0
- package/dist/api/routers/gallery.js +62 -0
- package/dist/api/routers/googleAnalytics.d.ts +31 -0
- package/dist/api/routers/googleAnalytics.d.ts.map +1 -0
- package/dist/api/routers/googleAnalytics.js +7 -0
- package/dist/api/routers/hasItemsSection.d.ts +140 -0
- package/dist/api/routers/hasItemsSection.d.ts.map +1 -0
- package/dist/api/routers/hasItemsSection.js +34 -0
- package/dist/api/routers/navigation.d.ts +52 -0
- package/dist/api/routers/navigation.d.ts.map +1 -0
- package/dist/api/routers/navigation.js +11 -0
- package/dist/api/routers/simpleSection.d.ts +58 -0
- package/dist/api/routers/simpleSection.d.ts.map +1 -0
- package/dist/api/routers/simpleSection.js +12 -0
- package/dist/api/trpc.d.ts +107 -0
- package/dist/api/trpc.d.ts.map +1 -0
- package/dist/api/trpc.js +72 -0
- package/dist/auth/axios/axiosInstance.d.ts +2 -0
- package/dist/auth/axios/axiosInstance.d.ts.map +1 -0
- package/dist/auth/axios/axiosInstance.js +8 -0
- package/dist/auth/csrf.d.ts +30 -0
- package/dist/auth/csrf.d.ts.map +1 -0
- package/dist/auth/csrf.js +76 -0
- package/dist/auth/hooks/index.d.ts +4 -0
- package/dist/auth/hooks/index.d.ts.map +1 -0
- package/dist/auth/hooks/index.js +3 -0
- package/dist/auth/hooks/useAxiosPrivate.d.ts +5 -0
- package/dist/auth/hooks/useAxiosPrivate.d.ts.map +1 -0
- package/dist/auth/hooks/useAxiosPrivate.js +74 -0
- package/dist/auth/hooks/useRefreshToken.d.ts +7 -0
- package/dist/auth/hooks/useRefreshToken.d.ts.map +1 -0
- package/dist/auth/hooks/useRefreshToken.js +79 -0
- package/dist/auth/index.d.ts +23 -0
- package/dist/auth/index.d.ts.map +1 -0
- package/dist/auth/index.js +44 -0
- package/dist/auth/jwt.d.ts +6 -0
- package/dist/auth/jwt.d.ts.map +1 -0
- package/dist/auth/jwt.js +25 -0
- package/dist/auth/lib/actions.d.ts +33 -0
- package/dist/auth/lib/actions.d.ts.map +1 -0
- package/dist/auth/lib/actions.js +209 -0
- package/dist/auth/lib/client.d.ts +4 -0
- package/dist/auth/lib/client.d.ts.map +1 -0
- package/dist/auth/lib/client.js +46 -0
- package/dist/auth/lib/index.d.ts +3 -0
- package/dist/auth/lib/index.d.ts.map +1 -0
- package/dist/auth/lib/index.js +2 -0
- package/dist/auth/react.d.ts +106 -0
- package/dist/auth/react.d.ts.map +1 -0
- package/dist/auth/react.js +347 -0
- package/dist/auth/trpc.d.ts +6 -0
- package/dist/auth/trpc.d.ts.map +1 -0
- package/dist/auth/trpc.js +81 -0
- package/dist/core/config/config-loader.d.ts +92 -0
- package/dist/core/config/config-loader.d.ts.map +1 -0
- package/dist/core/config/config-loader.js +230 -0
- package/dist/core/config/index.d.ts +3 -0
- package/dist/core/config/index.d.ts.map +1 -0
- package/dist/core/config/index.js +1 -0
- package/dist/core/config/loader.d.ts +2 -0
- package/dist/core/config/loader.d.ts.map +1 -0
- package/dist/core/config/loader.js +42 -0
- package/dist/core/db/index.d.ts +2 -0
- package/dist/core/db/index.d.ts.map +1 -0
- package/dist/core/db/index.js +1 -0
- package/dist/core/db/table-checker/DbTable.d.ts +6 -0
- package/dist/core/db/table-checker/DbTable.d.ts.map +1 -0
- package/dist/core/db/table-checker/DbTable.js +5 -0
- package/dist/core/db/table-checker/MysqlTable.d.ts +34 -0
- package/dist/core/db/table-checker/MysqlTable.d.ts.map +1 -0
- package/dist/core/db/table-checker/MysqlTable.js +102 -0
- package/dist/core/db/table-checker/index.d.ts +2 -0
- package/dist/core/db/table-checker/index.d.ts.map +1 -0
- package/dist/core/db/table-checker/index.js +1 -0
- package/dist/core/factories/FieldFactory.d.ts +124 -0
- package/dist/core/factories/FieldFactory.d.ts.map +1 -0
- package/dist/core/factories/FieldFactory.js +411 -0
- package/dist/core/factories/SectionFactory.d.ts +110 -0
- package/dist/core/factories/SectionFactory.d.ts.map +1 -0
- package/dist/core/factories/SectionFactory.js +415 -0
- package/dist/core/factories/index.d.ts +3 -0
- package/dist/core/factories/index.d.ts.map +1 -0
- package/dist/core/factories/index.js +2 -0
- package/dist/core/fields/checkbox.d.ts +63 -0
- package/dist/core/fields/checkbox.d.ts.map +1 -0
- package/dist/core/fields/checkbox.js +62 -0
- package/dist/core/fields/color.d.ts +84 -0
- package/dist/core/fields/color.d.ts.map +1 -0
- package/dist/core/fields/color.js +91 -0
- package/dist/core/fields/date.d.ts +100 -0
- package/dist/core/fields/date.d.ts.map +1 -0
- package/dist/core/fields/date.js +108 -0
- package/dist/core/fields/document.d.ts +180 -0
- package/dist/core/fields/document.d.ts.map +1 -0
- package/dist/core/fields/document.js +277 -0
- package/dist/core/fields/field-group.d.ts +18 -0
- package/dist/core/fields/field-group.d.ts.map +1 -0
- package/dist/core/fields/field-group.js +6 -0
- package/dist/core/fields/field.d.ts +126 -0
- package/dist/core/fields/field.d.ts.map +1 -0
- package/dist/core/fields/field.js +148 -0
- package/dist/core/fields/fileField.d.ts +15 -0
- package/dist/core/fields/fileField.d.ts.map +1 -0
- package/dist/core/fields/fileField.js +5 -0
- package/dist/core/fields/index.d.ts +65 -0
- package/dist/core/fields/index.d.ts.map +1 -0
- package/dist/core/fields/index.js +18 -0
- package/dist/core/fields/map.d.ts +167 -0
- package/dist/core/fields/map.d.ts.map +1 -0
- package/dist/core/fields/map.js +152 -0
- package/dist/core/fields/number.d.ts +186 -0
- package/dist/core/fields/number.d.ts.map +1 -0
- package/dist/core/fields/number.js +241 -0
- package/dist/core/fields/password.d.ts +109 -0
- package/dist/core/fields/password.d.ts.map +1 -0
- package/dist/core/fields/password.js +133 -0
- package/dist/core/fields/photo.d.ts +289 -0
- package/dist/core/fields/photo.d.ts.map +1 -0
- package/dist/core/fields/photo.js +410 -0
- package/dist/core/fields/richText.d.ts +295 -0
- package/dist/core/fields/richText.d.ts.map +1 -0
- package/dist/core/fields/richText.js +338 -0
- package/dist/core/fields/select.d.ts +366 -0
- package/dist/core/fields/select.d.ts.map +1 -0
- package/dist/core/fields/select.js +499 -0
- package/dist/core/fields/selectMultiple.d.ts +236 -0
- package/dist/core/fields/selectMultiple.d.ts.map +1 -0
- package/dist/core/fields/selectMultiple.js +417 -0
- package/dist/core/fields/tags.d.ts +131 -0
- package/dist/core/fields/tags.d.ts.map +1 -0
- package/dist/core/fields/tags.js +105 -0
- package/dist/core/fields/text.d.ts +136 -0
- package/dist/core/fields/text.d.ts.map +1 -0
- package/dist/core/fields/text.js +157 -0
- package/dist/core/fields/textArea.d.ts +107 -0
- package/dist/core/fields/textArea.d.ts.map +1 -0
- package/dist/core/fields/textArea.js +126 -0
- package/dist/core/fields/video.d.ts +148 -0
- package/dist/core/fields/video.d.ts.map +1 -0
- package/dist/core/fields/video.js +248 -0
- package/dist/core/helpers/entity.d.ts +8 -0
- package/dist/core/helpers/entity.d.ts.map +1 -0
- package/dist/core/helpers/entity.js +27 -0
- package/dist/core/helpers/index.d.ts +5 -0
- package/dist/core/helpers/index.d.ts.map +1 -0
- package/dist/core/helpers/index.js +3 -0
- package/dist/core/index.d.ts +8 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +7 -0
- package/dist/core/sections/category.d.ts +283 -0
- package/dist/core/sections/category.d.ts.map +1 -0
- package/dist/core/sections/category.js +147 -0
- package/dist/core/sections/hasItems.d.ts +632 -0
- package/dist/core/sections/hasItems.d.ts.map +1 -0
- package/dist/core/sections/hasItems.js +144 -0
- package/dist/core/sections/index.d.ts +5 -0
- package/dist/core/sections/index.d.ts.map +1 -0
- package/dist/core/sections/index.js +4 -0
- package/dist/core/sections/section.d.ts +226 -0
- package/dist/core/sections/section.d.ts.map +1 -0
- package/dist/core/sections/section.js +341 -0
- package/dist/core/sections/simple.d.ts +99 -0
- package/dist/core/sections/simple.d.ts.map +1 -0
- package/dist/core/sections/simple.js +95 -0
- package/dist/core/security/dom.d.ts +11 -0
- package/dist/core/security/dom.d.ts.map +1 -0
- package/dist/core/security/dom.js +92 -0
- package/dist/core/submit/ItemEditSubmit.d.ts +76 -0
- package/dist/core/submit/ItemEditSubmit.d.ts.map +1 -0
- package/dist/core/submit/ItemEditSubmit.js +186 -0
- package/dist/core/submit/NewItemSubmit.d.ts +14 -0
- package/dist/core/submit/NewItemSubmit.d.ts.map +1 -0
- package/dist/core/submit/NewItemSubmit.js +93 -0
- package/dist/core/submit/SimpleSectionSubmit.d.ts +13 -0
- package/dist/core/submit/SimpleSectionSubmit.d.ts.map +1 -0
- package/dist/core/submit/SimpleSectionSubmit.js +93 -0
- package/dist/core/submit/index.d.ts +5 -0
- package/dist/core/submit/index.d.ts.map +1 -0
- package/dist/core/submit/index.js +4 -0
- package/dist/core/submit/submit.d.ts +116 -0
- package/dist/core/submit/submit.d.ts.map +1 -0
- package/dist/core/submit/submit.js +479 -0
- package/dist/core/types/index.d.ts +280 -0
- package/dist/core/types/index.d.ts.map +1 -0
- package/dist/core/types/index.js +1 -0
- package/dist/db/client.d.ts +9 -0
- package/dist/db/client.d.ts.map +1 -0
- package/dist/db/client.js +19 -0
- package/dist/db/config.d.ts +6 -0
- package/dist/db/config.d.ts.map +1 -0
- package/dist/db/config.js +22 -0
- package/dist/db/drizzle.config.d.ts +6 -0
- package/dist/db/drizzle.config.d.ts.map +1 -0
- package/dist/db/drizzle.config.js +18 -0
- package/dist/db/index.d.ts +3 -0
- package/dist/db/index.d.ts.map +1 -0
- package/dist/db/index.js +3 -0
- package/dist/db/schema.d.ts +639 -0
- package/dist/db/schema.d.ts.map +1 -0
- package/dist/db/schema.js +73 -0
- package/dist/index.d.ts +7 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -1
- package/dist/translations/dictionaries/ar.json +279 -0
- package/dist/translations/dictionaries/en.json +279 -0
- package/dist/translations/index.d.ts +3 -0
- package/dist/translations/index.d.ts.map +1 -0
- package/dist/translations/index.js +15 -0
- package/dist/utils/CpanelApi.d.ts +25 -0
- package/dist/utils/CpanelApi.d.ts.map +1 -0
- package/dist/utils/CpanelApi.js +64 -0
- package/dist/utils/constants.d.ts +14 -0
- package/dist/utils/constants.d.ts.map +1 -0
- package/dist/utils/constants.js +61 -0
- package/dist/utils/index.d.ts +5 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +4 -0
- package/dist/utils/utils.d.ts +60 -0
- package/dist/utils/utils.d.ts.map +1 -0
- package/dist/utils/utils.js +132 -0
- package/dist/validators/checkbox.d.ts +4 -0
- package/dist/validators/checkbox.d.ts.map +1 -0
- package/dist/validators/checkbox.js +12 -0
- package/dist/validators/color.d.ts +4 -0
- package/dist/validators/color.d.ts.map +1 -0
- package/dist/validators/color.js +7 -0
- package/dist/validators/date.d.ts +4 -0
- package/dist/validators/date.d.ts.map +1 -0
- package/dist/validators/date.js +5 -0
- package/dist/validators/document.d.ts +4 -0
- package/dist/validators/document.d.ts.map +1 -0
- package/dist/validators/document.js +57 -0
- package/dist/validators/index.d.ts +15 -0
- package/dist/validators/index.d.ts.map +1 -0
- package/dist/validators/index.js +14 -0
- package/dist/validators/map.d.ts +4 -0
- package/dist/validators/map.d.ts.map +1 -0
- package/dist/validators/map.js +5 -0
- package/dist/validators/number.d.ts +4 -0
- package/dist/validators/number.d.ts.map +1 -0
- package/dist/validators/number.js +20 -0
- package/dist/validators/password.d.ts +4 -0
- package/dist/validators/password.d.ts.map +1 -0
- package/dist/validators/password.js +11 -0
- package/dist/validators/photo.d.ts +4 -0
- package/dist/validators/photo.d.ts.map +1 -0
- package/dist/validators/photo.js +100 -0
- package/dist/validators/richText.d.ts +4 -0
- package/dist/validators/richText.d.ts.map +1 -0
- package/dist/validators/richText.js +8 -0
- package/dist/validators/select-multiple.d.ts +10 -0
- package/dist/validators/select-multiple.d.ts.map +1 -0
- package/dist/validators/select-multiple.js +20 -0
- package/dist/validators/select.d.ts +4 -0
- package/dist/validators/select.d.ts.map +1 -0
- package/dist/validators/select.js +5 -0
- package/dist/validators/text.d.ts +4 -0
- package/dist/validators/text.d.ts.map +1 -0
- package/dist/validators/text.js +7 -0
- package/dist/validators/textarea.d.ts +4 -0
- package/dist/validators/textarea.d.ts.map +1 -0
- package/dist/validators/textarea.js +7 -0
- package/dist/validators/video.d.ts +4 -0
- package/dist/validators/video.d.ts.map +1 -0
- package/dist/validators/video.js +57 -0
- package/package.json +150 -6
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { getCsrfToken } from '../react';
|
|
4
|
+
export async function fetchData(path, req = {}) {
|
|
5
|
+
const url = `/api/auth/${path}`;
|
|
6
|
+
try {
|
|
7
|
+
const options = {
|
|
8
|
+
headers: {
|
|
9
|
+
// If there is a body, add the x-csrf-token header
|
|
10
|
+
...(req?.body ? { 'x-csrf-token': await getCsrfToken() } : {}),
|
|
11
|
+
'Content-Type': 'application/json',
|
|
12
|
+
...(req?.headers?.cookie ? { cookie: req.headers.cookie } : {}),
|
|
13
|
+
},
|
|
14
|
+
};
|
|
15
|
+
if (req?.body) {
|
|
16
|
+
options.body = JSON.stringify(req.body);
|
|
17
|
+
options.method = 'POST';
|
|
18
|
+
}
|
|
19
|
+
const res = await fetch(url, options);
|
|
20
|
+
const data = await res.json();
|
|
21
|
+
if (!res.ok)
|
|
22
|
+
throw data;
|
|
23
|
+
return data;
|
|
24
|
+
}
|
|
25
|
+
catch (error) {
|
|
26
|
+
console.error(error.message);
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
export function now() {
|
|
31
|
+
return Math.floor(Date.now() / 1000);
|
|
32
|
+
}
|
|
33
|
+
export function useOnline() {
|
|
34
|
+
const [isOnline, setIsOnline] = React.useState(typeof navigator !== 'undefined' ? navigator.onLine : false);
|
|
35
|
+
const setOnline = () => setIsOnline(true);
|
|
36
|
+
const setOffline = () => setIsOnline(false);
|
|
37
|
+
React.useEffect(() => {
|
|
38
|
+
window.addEventListener('online', setOnline);
|
|
39
|
+
window.addEventListener('offline', setOffline);
|
|
40
|
+
return () => {
|
|
41
|
+
window.removeEventListener('online', setOnline);
|
|
42
|
+
window.removeEventListener('offline', setOffline);
|
|
43
|
+
};
|
|
44
|
+
}, []);
|
|
45
|
+
return isOnline;
|
|
46
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/auth/lib/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,WAAW,CAAA;AAC7D,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAA"}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module react
|
|
3
|
+
*/
|
|
4
|
+
import * as React from 'react';
|
|
5
|
+
import type { Session } from './index';
|
|
6
|
+
import useAxiosPrivate from './hooks/useAxiosPrivate';
|
|
7
|
+
import useRefreshToken from './hooks/useRefreshToken';
|
|
8
|
+
export type UpdateSession = (data?: any) => Promise<Session | null>;
|
|
9
|
+
export interface SessionProviderProps {
|
|
10
|
+
children: React.ReactNode;
|
|
11
|
+
session?: Session | null;
|
|
12
|
+
/**
|
|
13
|
+
* A time interval (in seconds) after which the session will be re-fetched.
|
|
14
|
+
* If set to `0` (default), the session is not polled.
|
|
15
|
+
*/
|
|
16
|
+
refetchInterval?: number;
|
|
17
|
+
/**
|
|
18
|
+
* `SessionProvider` automatically re-fetches the session when the user switches between windows.
|
|
19
|
+
* This option activates this behaviour if set to `true` (default).
|
|
20
|
+
*/
|
|
21
|
+
refetchOnWindowFocus?: boolean;
|
|
22
|
+
/**
|
|
23
|
+
* Set to `false` to stop polling when the device has no internet access offline (determined by `navigator.onLine`)
|
|
24
|
+
*
|
|
25
|
+
* [`navigator.onLine` documentation](https://developer.mozilla.org/en-US/docs/Web/API/NavigatorOnLine/onLine)
|
|
26
|
+
*/
|
|
27
|
+
refetchWhenOffline?: false;
|
|
28
|
+
}
|
|
29
|
+
export interface UseSessionOptions<R extends boolean> {
|
|
30
|
+
required: R;
|
|
31
|
+
/** Defaults to `signIn` */
|
|
32
|
+
onUnauthenticated?: () => void;
|
|
33
|
+
}
|
|
34
|
+
export interface GetSessionParams {
|
|
35
|
+
event?: 'storage' | 'timer' | 'hidden' | string;
|
|
36
|
+
triggerEvent?: boolean;
|
|
37
|
+
broadcast?: boolean;
|
|
38
|
+
}
|
|
39
|
+
export interface AuthClientConfig {
|
|
40
|
+
_session?: Session | null | undefined;
|
|
41
|
+
/** Used for timestamp since last synced (in seconds) */
|
|
42
|
+
_lastSync: number;
|
|
43
|
+
/**
|
|
44
|
+
* Stores the `SessionProvider`'s session update method to be able to
|
|
45
|
+
* trigger session updates from places like `signIn` or `signOut`
|
|
46
|
+
*/
|
|
47
|
+
_getSession: (...args: any[]) => any;
|
|
48
|
+
}
|
|
49
|
+
export declare const __AUTH: AuthClientConfig;
|
|
50
|
+
export declare function logout(options?: {
|
|
51
|
+
deleteCookies?: boolean;
|
|
52
|
+
}): Promise<void>;
|
|
53
|
+
export declare function login({ username, password }: {
|
|
54
|
+
username: string;
|
|
55
|
+
password: string;
|
|
56
|
+
}): Promise<void>;
|
|
57
|
+
export declare function refreshSession(): Promise<void>;
|
|
58
|
+
/**
|
|
59
|
+
* Returns the current CSRF token.
|
|
60
|
+
* required to make requests that changes state.
|
|
61
|
+
*
|
|
62
|
+
* [CSRF Prevention: Double Submit Cookie](https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html#double-submit-cookie)
|
|
63
|
+
*/
|
|
64
|
+
export declare function getCsrfToken(): Promise<string>;
|
|
65
|
+
export declare function getSession(params?: GetSessionParams): Promise<Session | null>;
|
|
66
|
+
export type SessionContextValue<R extends boolean = false> = R extends true ? {
|
|
67
|
+
update: UpdateSession;
|
|
68
|
+
data: Session;
|
|
69
|
+
status: 'authenticated';
|
|
70
|
+
} | {
|
|
71
|
+
update: UpdateSession;
|
|
72
|
+
data: null;
|
|
73
|
+
status: 'loading';
|
|
74
|
+
} : {
|
|
75
|
+
update: UpdateSession;
|
|
76
|
+
data: Session;
|
|
77
|
+
status: 'authenticated';
|
|
78
|
+
} | {
|
|
79
|
+
update: UpdateSession;
|
|
80
|
+
data: null;
|
|
81
|
+
status: 'unauthenticated' | 'loading';
|
|
82
|
+
};
|
|
83
|
+
export declare const SessionContext: React.Context<{
|
|
84
|
+
update: UpdateSession;
|
|
85
|
+
data: Session;
|
|
86
|
+
status: "authenticated";
|
|
87
|
+
} | {
|
|
88
|
+
update: UpdateSession;
|
|
89
|
+
data: null;
|
|
90
|
+
status: "unauthenticated" | "loading";
|
|
91
|
+
} | undefined>;
|
|
92
|
+
export declare function useSession<R extends boolean>(options?: UseSessionOptions<R>): SessionContextValue<R>;
|
|
93
|
+
/**
|
|
94
|
+
* [React Context](https://react.dev/learn/passing-data-deeply-with-context) provider to wrap the app (`pages/`) to make session data available anywhere.
|
|
95
|
+
*
|
|
96
|
+
* When used, the session state is automatically synchronized across all open tabs/windows and they are all updated whenever they gain or lose focus
|
|
97
|
+
* or the state changes (e.g. a user signs in or out) when {@link SessionProviderProps.refetchOnWindowFocus} is `true`.
|
|
98
|
+
*
|
|
99
|
+
* :::info
|
|
100
|
+
* `SessionProvider` is for client-side use only and when using [Next.js App Router (`app/`)](https://nextjs.org/blog/next-13-4#nextjs-app-router) you should prefer the `auth()` export.
|
|
101
|
+
* :::
|
|
102
|
+
*/
|
|
103
|
+
export declare function SessionProvider(props: SessionProviderProps): React.JSX.Element;
|
|
104
|
+
export { useAxiosPrivate, useRefreshToken };
|
|
105
|
+
export { refreshTokenLink } from './trpc';
|
|
106
|
+
//# sourceMappingURL=react.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"react.d.ts","sourceRoot":"","sources":["../../src/auth/react.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,347 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module react
|
|
3
|
+
*/
|
|
4
|
+
'use client';
|
|
5
|
+
import * as React from 'react';
|
|
6
|
+
import { fetchData, now, useOnline } from './lib/client';
|
|
7
|
+
import { useRouter } from 'next/navigation';
|
|
8
|
+
import useAxiosPrivate from './hooks/useAxiosPrivate';
|
|
9
|
+
import useRefreshToken from './hooks/useRefreshToken';
|
|
10
|
+
export const __AUTH = {
|
|
11
|
+
_lastSync: 0,
|
|
12
|
+
_session: undefined,
|
|
13
|
+
_getSession: () => { },
|
|
14
|
+
};
|
|
15
|
+
export async function logout(options) {
|
|
16
|
+
const { deleteCookies = true } = options ?? {};
|
|
17
|
+
if (deleteCookies) {
|
|
18
|
+
/**
|
|
19
|
+
* Send a request to the server to remove the session.
|
|
20
|
+
* This will delete the cookies.
|
|
21
|
+
* It will also delete the session from the database if auth() is not null.
|
|
22
|
+
*/
|
|
23
|
+
await fetch('/api/auth', {
|
|
24
|
+
method: 'DELETE',
|
|
25
|
+
headers: {
|
|
26
|
+
'Content-Type': 'application/json',
|
|
27
|
+
'x-csrf-token': await getCsrfToken(),
|
|
28
|
+
},
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Broadcast a message to all other tabs/windows to tell them the user has logged out.
|
|
33
|
+
* This is to ensure all tabs/windows trigger a session update.
|
|
34
|
+
*/
|
|
35
|
+
broadcast().postMessage({ event: 'session', data: { trigger: 'logout' } });
|
|
36
|
+
/**
|
|
37
|
+
* Remove the session in the current tab/window.
|
|
38
|
+
*/
|
|
39
|
+
await __AUTH._getSession({ event: 'logout' });
|
|
40
|
+
}
|
|
41
|
+
export async function login({ username, password }) {
|
|
42
|
+
const response = await fetch('/api/auth', {
|
|
43
|
+
method: 'POST',
|
|
44
|
+
headers: {
|
|
45
|
+
'Content-Type': 'application/json',
|
|
46
|
+
'x-csrf-token': await getCsrfToken(),
|
|
47
|
+
},
|
|
48
|
+
body: JSON.stringify({ username, password }),
|
|
49
|
+
});
|
|
50
|
+
if (!response.ok) {
|
|
51
|
+
const error = await response.json();
|
|
52
|
+
throw new Error(error.error);
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Broadcast a message to all other tabs/windows to tell them the user has logged in.
|
|
56
|
+
* This is to ensure all tabs/windows trigger a session update.
|
|
57
|
+
*/
|
|
58
|
+
broadcast().postMessage({ event: 'session', data: { trigger: 'login' } });
|
|
59
|
+
/**
|
|
60
|
+
* Update the session in the current tab/window.
|
|
61
|
+
*/
|
|
62
|
+
await __AUTH._getSession({ event: 'storage' });
|
|
63
|
+
}
|
|
64
|
+
export async function refreshSession() {
|
|
65
|
+
/**
|
|
66
|
+
* Broadcast a message to all other tabs/windows to tell them the session has been updated.
|
|
67
|
+
* This is to ensure all tabs/windows trigger a session update.
|
|
68
|
+
*/
|
|
69
|
+
broadcast().postMessage({ event: 'session', data: { trigger: 'refresh' } });
|
|
70
|
+
/**
|
|
71
|
+
* Update the session in the current tab/window.
|
|
72
|
+
*/
|
|
73
|
+
await __AUTH._getSession({ event: 'storage' });
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Returns the current CSRF token.
|
|
77
|
+
* required to make requests that changes state.
|
|
78
|
+
*
|
|
79
|
+
* [CSRF Prevention: Double Submit Cookie](https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html#double-submit-cookie)
|
|
80
|
+
*/
|
|
81
|
+
export async function getCsrfToken() {
|
|
82
|
+
const response = await fetchData('csrf');
|
|
83
|
+
return response?.csrfToken ?? '';
|
|
84
|
+
}
|
|
85
|
+
let broadcastChannel = null;
|
|
86
|
+
function getNewBroadcastChannel() {
|
|
87
|
+
return new BroadcastChannel('lzcms-auth');
|
|
88
|
+
}
|
|
89
|
+
function broadcast() {
|
|
90
|
+
if (typeof BroadcastChannel === 'undefined') {
|
|
91
|
+
return {
|
|
92
|
+
postMessage: () => { },
|
|
93
|
+
addEventListener: () => { },
|
|
94
|
+
removeEventListener: () => { },
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
if (broadcastChannel === null) {
|
|
98
|
+
broadcastChannel = getNewBroadcastChannel();
|
|
99
|
+
}
|
|
100
|
+
return broadcastChannel;
|
|
101
|
+
}
|
|
102
|
+
export async function getSession(params) {
|
|
103
|
+
const session = await fetchData('session', params);
|
|
104
|
+
if (params?.broadcast ?? true) {
|
|
105
|
+
const broadcastChannel = getNewBroadcastChannel();
|
|
106
|
+
broadcastChannel.postMessage({
|
|
107
|
+
event: 'session',
|
|
108
|
+
data: { trigger: 'getSession' },
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
return session;
|
|
112
|
+
}
|
|
113
|
+
export const SessionContext = React.createContext?.(undefined);
|
|
114
|
+
export function useSession(options) {
|
|
115
|
+
const router = useRouter();
|
|
116
|
+
if (!SessionContext) {
|
|
117
|
+
throw new Error('React Context is unavailable in Server Components');
|
|
118
|
+
}
|
|
119
|
+
// @ts-expect-error Satisfy TS if branch on line below
|
|
120
|
+
const value = React.useContext(SessionContext);
|
|
121
|
+
if (!value && process.env.NODE_ENV !== 'production') {
|
|
122
|
+
throw new Error('[next-auth]: `useSession` must be wrapped in a <SessionProvider />');
|
|
123
|
+
}
|
|
124
|
+
const { required, onUnauthenticated } = options ?? {};
|
|
125
|
+
const requiredAndNotLoading = required && value.status === 'unauthenticated';
|
|
126
|
+
React.useEffect(() => {
|
|
127
|
+
if (requiredAndNotLoading) {
|
|
128
|
+
const url = `/auth/login?${new URLSearchParams({
|
|
129
|
+
callbackUrl: window.location.href,
|
|
130
|
+
})}`;
|
|
131
|
+
if (onUnauthenticated)
|
|
132
|
+
onUnauthenticated();
|
|
133
|
+
else
|
|
134
|
+
router.push(url);
|
|
135
|
+
}
|
|
136
|
+
}, [requiredAndNotLoading, onUnauthenticated]);
|
|
137
|
+
if (requiredAndNotLoading) {
|
|
138
|
+
return {
|
|
139
|
+
data: value.data,
|
|
140
|
+
update: value.update,
|
|
141
|
+
status: 'loading',
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
return value;
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* [React Context](https://react.dev/learn/passing-data-deeply-with-context) provider to wrap the app (`pages/`) to make session data available anywhere.
|
|
148
|
+
*
|
|
149
|
+
* When used, the session state is automatically synchronized across all open tabs/windows and they are all updated whenever they gain or lose focus
|
|
150
|
+
* or the state changes (e.g. a user signs in or out) when {@link SessionProviderProps.refetchOnWindowFocus} is `true`.
|
|
151
|
+
*
|
|
152
|
+
* :::info
|
|
153
|
+
* `SessionProvider` is for client-side use only and when using [Next.js App Router (`app/`)](https://nextjs.org/blog/next-13-4#nextjs-app-router) you should prefer the `auth()` export.
|
|
154
|
+
* :::
|
|
155
|
+
*/
|
|
156
|
+
export function SessionProvider(props) {
|
|
157
|
+
if (!SessionContext) {
|
|
158
|
+
throw new Error('React Context is unavailable in Server Components');
|
|
159
|
+
}
|
|
160
|
+
const axiosPrivate = useAxiosPrivate({
|
|
161
|
+
refreshTokenOn: 404,
|
|
162
|
+
});
|
|
163
|
+
const { children, refetchInterval, refetchWhenOffline } = props;
|
|
164
|
+
/**
|
|
165
|
+
* If session was `null`, there was an attempt to fetch it,
|
|
166
|
+
* but it failed, but we still treat it as a valid initial value.
|
|
167
|
+
*/
|
|
168
|
+
const hasInitialSession = props.session !== undefined;
|
|
169
|
+
/** If session was passed, initialize as already synced */
|
|
170
|
+
__AUTH._lastSync = hasInitialSession ? now() : 0;
|
|
171
|
+
const [session, setSession] = React.useState(() => {
|
|
172
|
+
if (hasInitialSession)
|
|
173
|
+
__AUTH._session = props.session;
|
|
174
|
+
return props.session;
|
|
175
|
+
});
|
|
176
|
+
/** If session was passed, initialize as not loading */
|
|
177
|
+
const [loading, setLoading] = React.useState(!hasInitialSession);
|
|
178
|
+
React.useEffect(() => {
|
|
179
|
+
async function generateInitialSession() {
|
|
180
|
+
/**
|
|
181
|
+
* Use the `axiosPrivate` instance to make a request to `/api/auth/session` to get the
|
|
182
|
+
* first session data. The reason for using `axiosPrivate` is to make sure that the client
|
|
183
|
+
* will initiate a refresh token process (with the refresh token cookie) if the access
|
|
184
|
+
* token cookie is expired. (TRPC custom refresh link can be used as well)
|
|
185
|
+
*/
|
|
186
|
+
try {
|
|
187
|
+
const response = await axiosPrivate.get('/auth/session');
|
|
188
|
+
return response.data;
|
|
189
|
+
}
|
|
190
|
+
catch (error) {
|
|
191
|
+
return null;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
async function init() {
|
|
195
|
+
/**
|
|
196
|
+
* Make sure the session is created at the start of the app.
|
|
197
|
+
* Is session is not set, this means this is the first time
|
|
198
|
+
* the app is being loaded.
|
|
199
|
+
*/
|
|
200
|
+
if (__AUTH._session === undefined) {
|
|
201
|
+
const session = await generateInitialSession();
|
|
202
|
+
__AUTH._lastSync = now();
|
|
203
|
+
return session;
|
|
204
|
+
}
|
|
205
|
+
return __AUTH._session;
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Initialize the session.
|
|
209
|
+
*/
|
|
210
|
+
init()
|
|
211
|
+
.then(async (session) => {
|
|
212
|
+
if (session) {
|
|
213
|
+
__AUTH._session = session;
|
|
214
|
+
setSession(session);
|
|
215
|
+
}
|
|
216
|
+
else {
|
|
217
|
+
/**
|
|
218
|
+
* It's not necessary to log out the user and broadcast the logout event,
|
|
219
|
+
* because there are no cookies set to delete.
|
|
220
|
+
* But it's a good practice to do so.
|
|
221
|
+
*/
|
|
222
|
+
// await logout()
|
|
223
|
+
}
|
|
224
|
+
})
|
|
225
|
+
.then(() => {
|
|
226
|
+
__AUTH._getSession = async ({ event } = {}) => {
|
|
227
|
+
try {
|
|
228
|
+
/**
|
|
229
|
+
* If the event is a logout event, we should clear the session and return early.
|
|
230
|
+
*/
|
|
231
|
+
if (event === 'logout') {
|
|
232
|
+
__AUTH._lastSync = now();
|
|
233
|
+
__AUTH._session = null;
|
|
234
|
+
setSession(null);
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* If the event is a storage event, we should update the session and not broadcast the event.
|
|
239
|
+
* Storage events can also be triggered when a broadcast message is sent from another
|
|
240
|
+
* tab/window. (see below)
|
|
241
|
+
*/
|
|
242
|
+
if (event === 'storage') {
|
|
243
|
+
__AUTH._lastSync = now();
|
|
244
|
+
__AUTH._session = await getSession({
|
|
245
|
+
broadcast: false,
|
|
246
|
+
});
|
|
247
|
+
setSession(__AUTH._session);
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
if (
|
|
251
|
+
// If there is no time defined for when a session should be considered
|
|
252
|
+
// stale, then it's okay to use the value we have until an event is
|
|
253
|
+
// triggered which updates it
|
|
254
|
+
!event ||
|
|
255
|
+
// If the client doesn't have a session then we don't need to call
|
|
256
|
+
// the server to check if it does (if they have signed in via another
|
|
257
|
+
// tab or window that will come through as a "storage" event anyway)
|
|
258
|
+
__AUTH._session === null ||
|
|
259
|
+
// Bail out early if the client session is not stale yet
|
|
260
|
+
now() < __AUTH._lastSync) {
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
// An event or session staleness occurred, update the client session.
|
|
264
|
+
__AUTH._lastSync = now();
|
|
265
|
+
__AUTH._session = await getSession();
|
|
266
|
+
setSession(__AUTH._session);
|
|
267
|
+
}
|
|
268
|
+
catch (error) {
|
|
269
|
+
__AUTH._lastSync = now();
|
|
270
|
+
__AUTH._session = null;
|
|
271
|
+
setSession(null);
|
|
272
|
+
// console.error('Error fetching session', error)
|
|
273
|
+
}
|
|
274
|
+
finally {
|
|
275
|
+
setLoading(false);
|
|
276
|
+
}
|
|
277
|
+
};
|
|
278
|
+
})
|
|
279
|
+
.finally(() => setLoading(false));
|
|
280
|
+
return () => {
|
|
281
|
+
__AUTH._lastSync = 0;
|
|
282
|
+
__AUTH._session = undefined;
|
|
283
|
+
__AUTH._getSession = () => { };
|
|
284
|
+
};
|
|
285
|
+
}, []);
|
|
286
|
+
React.useEffect(() => {
|
|
287
|
+
const handle = () => __AUTH._getSession({ event: 'storage' });
|
|
288
|
+
// Listen for storage events and update session if event fired from
|
|
289
|
+
// another window (but suppress firing another event to avoid a loop)
|
|
290
|
+
// Fetch new session data but tell it not to fire another event to
|
|
291
|
+
// avoid an infinite loop.
|
|
292
|
+
// Note: We could pass session data through and do something like
|
|
293
|
+
// `setData(message.data)` but that can cause problems depending
|
|
294
|
+
// on how the session object is being used in the client; it is
|
|
295
|
+
// more robust to have each window/tab fetch it's own copy of the
|
|
296
|
+
// session object rather than share it across instances.
|
|
297
|
+
broadcast().addEventListener('message', handle);
|
|
298
|
+
return () => broadcast().removeEventListener('message', handle);
|
|
299
|
+
}, []);
|
|
300
|
+
React.useEffect(() => {
|
|
301
|
+
const { refetchOnWindowFocus = true } = props;
|
|
302
|
+
// Listen for when the page is visible, if the user switches tabs
|
|
303
|
+
// and makes our tab visible again, re-fetch the session, but only if
|
|
304
|
+
// this feature is not disabled.
|
|
305
|
+
const visibilityHandler = () => {
|
|
306
|
+
if (refetchOnWindowFocus && document.visibilityState === 'visible')
|
|
307
|
+
__AUTH._getSession({ event: 'visibilitychange' });
|
|
308
|
+
};
|
|
309
|
+
document.addEventListener('visibilitychange', visibilityHandler, false);
|
|
310
|
+
return () => document.removeEventListener('visibilitychange', visibilityHandler, false);
|
|
311
|
+
}, [props.refetchOnWindowFocus]);
|
|
312
|
+
const isOnline = useOnline();
|
|
313
|
+
// TODO: Flip this behavior in next major version
|
|
314
|
+
const shouldRefetch = refetchWhenOffline !== false || isOnline;
|
|
315
|
+
React.useEffect(() => {
|
|
316
|
+
if (refetchInterval && shouldRefetch) {
|
|
317
|
+
const refetchIntervalTimer = setInterval(() => {
|
|
318
|
+
if (__AUTH._session) {
|
|
319
|
+
__AUTH._getSession({ event: 'poll' });
|
|
320
|
+
}
|
|
321
|
+
}, refetchInterval * 1000);
|
|
322
|
+
return () => clearInterval(refetchIntervalTimer);
|
|
323
|
+
}
|
|
324
|
+
}, [refetchInterval, shouldRefetch]);
|
|
325
|
+
const value = React.useMemo(() => ({
|
|
326
|
+
data: session,
|
|
327
|
+
status: loading ? 'loading' : session ? 'authenticated' : 'unauthenticated',
|
|
328
|
+
async update(data) {
|
|
329
|
+
if (loading)
|
|
330
|
+
return;
|
|
331
|
+
setLoading(true);
|
|
332
|
+
const newSession = await fetchData('session', typeof data === 'undefined' ? undefined : { body: { data } });
|
|
333
|
+
setLoading(false);
|
|
334
|
+
if (newSession) {
|
|
335
|
+
setSession(newSession);
|
|
336
|
+
broadcast().postMessage({
|
|
337
|
+
event: 'session',
|
|
338
|
+
data: { trigger: 'getSession' },
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
return newSession;
|
|
342
|
+
},
|
|
343
|
+
}), [session, loading]);
|
|
344
|
+
return React.createElement(SessionContext.Provider, { value: value }, children);
|
|
345
|
+
}
|
|
346
|
+
export { useAxiosPrivate, useRefreshToken };
|
|
347
|
+
export { refreshTokenLink } from './trpc';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"trpc.d.ts","sourceRoot":"","sources":["../../src/auth/trpc.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AAM5C;;GAEG;AACH,eAAO,MAAM,gBAAgB,QAAO,QAAQ,CAAC,aAAa,CAAA,GAAG,CA4E5D,CAAA"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { observable } from '@trpc/server/observable';
|
|
2
|
+
// import type { AppRouter } from '../api'
|
|
3
|
+
import { logout, refreshSession } from './react';
|
|
4
|
+
/**
|
|
5
|
+
* tRPC link that refreshes the token if it's expired
|
|
6
|
+
*/
|
|
7
|
+
export const refreshTokenLink = () => {
|
|
8
|
+
/**
|
|
9
|
+
* Add a lock state to prevent multiple refreshes at the same time
|
|
10
|
+
*/
|
|
11
|
+
let isRefreshing = false;
|
|
12
|
+
let refreshPromise = null;
|
|
13
|
+
return () => {
|
|
14
|
+
return ({ next, op }) => {
|
|
15
|
+
return observable((observer) => {
|
|
16
|
+
let next$ = null;
|
|
17
|
+
let shouldRetry = true; // Flag to control the retry
|
|
18
|
+
function attempt() {
|
|
19
|
+
next$?.unsubscribe();
|
|
20
|
+
next$ = next(op).subscribe({
|
|
21
|
+
async error(err) {
|
|
22
|
+
if (err.data?.code === 'UNAUTHORIZED' && shouldRetry) {
|
|
23
|
+
if (!isRefreshing) {
|
|
24
|
+
isRefreshing = true;
|
|
25
|
+
refreshPromise = (async () => {
|
|
26
|
+
try {
|
|
27
|
+
const response = await fetch('/api/auth/refresh');
|
|
28
|
+
if (response.status !== 200) {
|
|
29
|
+
await logout({
|
|
30
|
+
/**
|
|
31
|
+
* No need to delete the cookies, because they are both invalid.
|
|
32
|
+
*/
|
|
33
|
+
deleteCookies: false,
|
|
34
|
+
});
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
await refreshSession();
|
|
38
|
+
}
|
|
39
|
+
catch (e) {
|
|
40
|
+
await logout({
|
|
41
|
+
/**
|
|
42
|
+
* No need to delete the cookies, because they are both invalid.
|
|
43
|
+
*/
|
|
44
|
+
deleteCookies: false,
|
|
45
|
+
});
|
|
46
|
+
throw e;
|
|
47
|
+
}
|
|
48
|
+
finally {
|
|
49
|
+
isRefreshing = false;
|
|
50
|
+
refreshPromise = null;
|
|
51
|
+
}
|
|
52
|
+
})();
|
|
53
|
+
}
|
|
54
|
+
try {
|
|
55
|
+
await refreshPromise;
|
|
56
|
+
shouldRetry = false;
|
|
57
|
+
attempt();
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
catch (e) {
|
|
61
|
+
// Don't throw under-the-hood refresh flow's errors
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
observer.error(err);
|
|
65
|
+
},
|
|
66
|
+
next(value) {
|
|
67
|
+
observer.next(value);
|
|
68
|
+
},
|
|
69
|
+
complete() {
|
|
70
|
+
observer.complete();
|
|
71
|
+
},
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
attempt();
|
|
75
|
+
return () => {
|
|
76
|
+
next$?.unsubscribe();
|
|
77
|
+
};
|
|
78
|
+
});
|
|
79
|
+
};
|
|
80
|
+
};
|
|
81
|
+
};
|