diff --git a/.env.example b/.env.example index b1f36f0..6e5a647 100644 --- a/.env.example +++ b/.env.example @@ -1 +1,8 @@ +# postgresql url DATABASE_URL="postgresql://johndoe:randompassword@localhost:5432/mydb?schema=public" + +# Whether allow register user +ALLOW_REGISTER= + +# For analyze tianji self +WEBSITE_ID= diff --git a/src/client/App.tsx b/src/client/App.tsx index e97216b..8a71b66 100644 --- a/src/client/App.tsx +++ b/src/client/App.tsx @@ -13,9 +13,14 @@ import React from 'react'; import { trpc, trpcClient } from './api/trpc'; import { MonitorPage } from './pages/Monitor'; import { WebsitePage } from './pages/Website'; +import { useGlobalConfig } from './hooks/useConfig'; +import { useInjectWebsiteScript } from './hooks/useInjectWebsiteScript'; export const AppRoutes: React.FC = React.memo(() => { const { info } = useUserStore(); + const { allowRegister } = useGlobalConfig(); + + useInjectWebsiteScript(); return ( @@ -30,7 +35,7 @@ export const AppRoutes: React.FC = React.memo(() => { ) : ( } /> - } /> + {allowRegister && } />} )} diff --git a/src/client/hooks/useConfig.ts b/src/client/hooks/useConfig.ts new file mode 100644 index 0000000..af58930 --- /dev/null +++ b/src/client/hooks/useConfig.ts @@ -0,0 +1,19 @@ +import { AppRouterOutput, trpc } from '../api/trpc'; + +const defaultGlobalConfig: AppRouterOutput['global']['config'] = { + allowRegister: false, +}; + +/** + * Fetch settings from server + */ +export function useGlobalConfig(): AppRouterOutput['global']['config'] { + const { data = defaultGlobalConfig } = trpc.global.config.useQuery( + undefined, + { + staleTime: 1000 * 60 * 60 * 1, // 1 hour + } + ); + + return data; +} diff --git a/src/client/hooks/useDataReady.tsx b/src/client/hooks/useDataReady.tsx new file mode 100644 index 0000000..56ca5b6 --- /dev/null +++ b/src/client/hooks/useDataReady.tsx @@ -0,0 +1,27 @@ +import { DependencyList, useLayoutEffect, useRef } from 'react'; +import { useEvent } from './useEvent'; + +/** + * Call once on data ready(validator return true) + */ +export function useDataReady( + validator: () => boolean, + cb: () => void, + deps?: DependencyList +) { + const isReadyRef = useRef(false); + + const _validator = useEvent(validator); + const _callback = useEvent(cb); + + useLayoutEffect(() => { + if (isReadyRef.current) { + return; + } + + if (_validator() === true) { + _callback(); + isReadyRef.current = true; + } + }, deps); +} diff --git a/src/client/hooks/useInjectWebsiteScript.ts b/src/client/hooks/useInjectWebsiteScript.ts new file mode 100644 index 0000000..f57b258 --- /dev/null +++ b/src/client/hooks/useInjectWebsiteScript.ts @@ -0,0 +1,16 @@ +import { useGlobalConfig } from './useConfig'; +import { useDataReady } from './useDataReady'; + +export function useInjectWebsiteScript() { + const { websiteId } = useGlobalConfig(); + + useDataReady( + () => typeof websiteId === 'string' && websiteId.length > 0, + () => { + const el = document.createElement('script'); + el.src = location.origin + '/tracker.js'; + el.setAttribute('data-website-id', String(websiteId)); + document.head.append(el); + } + ); +} diff --git a/src/client/pages/Login.tsx b/src/client/pages/Login.tsx index 48e915e..3bd8f2e 100644 --- a/src/client/pages/Login.tsx +++ b/src/client/pages/Login.tsx @@ -5,6 +5,7 @@ import { useRequest } from '../hooks/useRequest'; import { trpc } from '../api/trpc'; import { setJWT } from '../api/auth'; import { setUserInfo } from '../store/user'; +import { useGlobalConfig } from '../hooks/useConfig'; export const Login: React.FC = React.memo(() => { const navigate = useNavigate(); @@ -20,6 +21,7 @@ export const Login: React.FC = React.memo(() => { setUserInfo(res.info); navigate('/dashboard'); }); + const { allowRegister } = useGlobalConfig(); return (
@@ -51,18 +53,21 @@ export const Login: React.FC = React.memo(() => { Login - - - + + {allowRegister && ( + + + + )}
diff --git a/src/server/trpc/routers/global.ts b/src/server/trpc/routers/global.ts new file mode 100644 index 0000000..8720889 --- /dev/null +++ b/src/server/trpc/routers/global.ts @@ -0,0 +1,14 @@ +import { publicProcedure, router } from '../trpc'; + +export const globalRouter = router({ + config: publicProcedure.query(async ({ input }) => { + return { + allowRegister: checkEnvTrusty(process.env.ALLOW_REGISTER), + websiteId: process.env.WEBSITE_ID, + }; + }), +}); + +function checkEnvTrusty(env: string | undefined): boolean { + return env === '1' || env === 'true'; +} diff --git a/src/server/trpc/routers/index.ts b/src/server/trpc/routers/index.ts index c419132..1bd53be 100644 --- a/src/server/trpc/routers/index.ts +++ b/src/server/trpc/routers/index.ts @@ -4,8 +4,10 @@ import { websiteRouter } from './website'; import { monitorRouter } from './monitor'; import { userRouter } from './user'; import { workspaceRouter } from './workspace'; +import { globalRouter } from './global'; export const appRouter = router({ + global: globalRouter, user: userRouter, workspace: workspaceRouter, website: websiteRouter,