@wzyjs/cli 0.3.31 → 0.3.36
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/dist/index.js +350 -35
- package/package.json +6 -4
- package/template/web/base/Dockerfile +37 -0
- package/template/web/base/_env +2 -0
- package/template/web/base/eslint.config.js +94 -0
- package/template/web/base/next.config.js +29 -0
- package/template/web/base/package.json +53 -0
- package/template/web/base/postcss.config.js +5 -0
- package/template/web/base/prisma/models/demo/DemoItem.zmodel +8 -0
- package/template/web/base/prisma/schema.prisma +29 -0
- package/template/web/base/prisma/schema.zmodel +1 -0
- package/template/web/base/src/app/api/trpc/[trpc]/route.ts +24 -0
- package/template/web/base/src/app/auth/disabled/page.tsx +29 -0
- package/template/web/base/src/app/auth/error/page.tsx +47 -0
- package/template/web/base/src/app/auth/signin/page.tsx +53 -0
- package/template/web/base/src/app/auth/unauthorized/page.tsx +60 -0
- package/template/web/base/src/app/demo/page.tsx +59 -0
- package/template/web/base/src/app/layout.tsx +32 -0
- package/template/web/base/src/app/not-found.tsx +21 -0
- package/template/web/base/src/app/page.tsx +92 -0
- package/template/web/base/src/components/base/Layout/Header/context.tsx +56 -0
- package/template/web/base/src/components/base/Layout/Header/index.tsx +32 -0
- package/template/web/base/src/components/base/Layout/index.tsx +27 -0
- package/template/web/base/src/components/base/Providers/TRPCReactProvider.tsx +25 -0
- package/template/web/base/src/components/base/Providers/index.tsx +31 -0
- package/template/web/base/src/components/base/display/DisplayProvider.tsx +44 -0
- package/template/web/base/src/components/base/display/consts.ts +10 -0
- package/template/web/base/src/components/base/display/context.ts +6 -0
- package/template/web/base/src/components/base/display/index.ts +4 -0
- package/template/web/base/src/components/base/display/types.ts +12 -0
- package/template/web/base/src/components/base/display/useDisplay.ts +9 -0
- package/template/web/base/src/components/base/display/utils.ts +18 -0
- package/template/web/base/src/components/base/theme/ThemeProvider.tsx +83 -0
- package/template/web/base/src/components/base/theme/ThemeToggle.tsx +26 -0
- package/template/web/base/src/components/base/theme/consts.tsx +59 -0
- package/template/web/base/src/components/base/theme/context.ts +14 -0
- package/template/web/base/src/components/base/theme/useAppTheme.ts +15 -0
- package/template/web/base/src/components/base/theme/utils.ts +17 -0
- package/template/web/base/src/components/index.ts +1 -0
- package/template/web/base/src/consts/index.ts +6 -0
- package/template/web/base/src/enums/index.ts +1 -0
- package/template/web/base/src/env.js +44 -0
- package/template/web/base/src/hooks/index.ts +1 -0
- package/template/web/base/src/server/db/client.ts +19 -0
- package/template/web/base/src/server/routers/index.ts +3 -0
- package/template/web/base/src/server/trpc/context.ts +14 -0
- package/template/web/base/src/server/trpc/index.ts +1 -0
- package/template/web/base/src/server/trpc/init.ts +27 -0
- package/template/web/base/src/server/trpc/procedures.ts +5 -0
- package/template/web/base/src/server/trpc/router.ts +11 -0
- package/template/web/base/src/server/utils/index.ts +1 -0
- package/template/web/base/src/styles/globals.css +3 -0
- package/template/web/base/src/types/index.ts +1 -0
- package/template/web/base/src/utils/index.ts +4 -0
- package/template/web/base/src/utils/query-client/index.ts +31 -0
- package/template/web/base/src/utils/trpc/index.ts +23 -0
- package/template/web/base/src/utils/trpc/utils.ts +61 -0
- package/template/web/base/tailwind.config.cjs +7 -0
- package/template/web/base/tsconfig.json +46 -0
- package/template/web/google/_env +8 -0
- package/template/web/google/package.json +55 -0
- package/template/web/google/prisma/models/auth/Account.zmodel +26 -0
- package/template/web/google/prisma/models/auth/Session.zmodel +17 -0
- package/template/web/google/prisma/models/auth/User.zmodel +22 -0
- package/template/web/google/prisma/schema.zmodel +2 -0
- package/template/web/google/src/app/api/auth/[...nextauth]/route.ts +7 -0
- package/template/web/google/src/app/auth/error/page.tsx +56 -0
- package/template/web/google/src/app/auth/signin/page.tsx +83 -0
- package/template/web/google/src/app/layout.tsx +35 -0
- package/template/web/google/src/components/base/AuthGuard/Loading.tsx +19 -0
- package/template/web/google/src/components/base/AuthGuard/index.tsx +45 -0
- package/template/web/google/src/components/base/Layout/Header/UserAuthStatus.tsx +93 -0
- package/template/web/google/src/components/base/Layout/Header/index.tsx +34 -0
- package/template/web/google/src/components/base/Providers/index.tsx +34 -0
- package/template/web/google/src/env.js +52 -0
- package/template/web/google/src/server/auth/next-auth/adapter.ts +77 -0
- package/template/web/google/src/server/auth/next-auth/options.ts +53 -0
- package/template/web/google/src/server/trpc/context.ts +21 -0
- package/template/web/google/src/server/trpc/procedures.ts +16 -0
- package/template/web/middle/_env +10 -0
- package/template/web/middle/package.json +55 -0
- package/template/web/middle/src/app/api/auth/[...nextauth]/route.ts +7 -0
- package/template/web/middle/src/app/auth/disabled/page.tsx +71 -0
- package/template/web/middle/src/app/auth/error/page.tsx +56 -0
- package/template/web/middle/src/app/auth/signin/page.tsx +91 -0
- package/template/web/middle/src/app/auth/unauthorized/page.tsx +88 -0
- package/template/web/middle/src/app/layout.tsx +35 -0
- package/template/web/middle/src/app/middle/page.tsx +10 -0
- package/template/web/middle/src/app/page.tsx +20 -0
- package/template/web/middle/src/components/base/AuthGuard/Loading.tsx +19 -0
- package/template/web/middle/src/components/base/AuthGuard/index.tsx +45 -0
- package/template/web/middle/src/components/base/Layout/Header/UserAuthStatus.tsx +93 -0
- package/template/web/middle/src/components/base/Layout/Header/index.tsx +34 -0
- package/template/web/middle/src/components/base/Layout/Sidebar/index.tsx +88 -0
- package/template/web/middle/src/components/base/Layout/index.tsx +35 -0
- package/template/web/middle/src/components/base/Providers/MiddleProvider.tsx +33 -0
- package/template/web/middle/src/components/base/Providers/index.tsx +37 -0
- package/template/web/middle/src/env.js +57 -0
- package/template/web/middle/src/server/auth/next-auth/options.ts +26 -0
- package/template/web/middle/src/server/trpc/context.ts +22 -0
- package/template/web/middle/src/server/trpc/procedures.ts +16 -0
- package/template/web/middle/src/server/utils/index.ts +2 -0
- package/template/web/middle/src/server/utils/middle.ts +34 -0
- package/template/web/middle/src/types/index.ts +3 -0
- package/template/web/middle/src/utils/index.ts +5 -0
- package/template/web/middle/src/utils/middle.ts +28 -0
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { type ReactNode } from 'react'
|
|
4
|
+
|
|
5
|
+
import { LayoutHeader } from './Header'
|
|
6
|
+
import { LayoutHeaderProvider } from './Header/context'
|
|
7
|
+
import { useDisplay } from '../display'
|
|
8
|
+
|
|
9
|
+
interface LayoutProps {
|
|
10
|
+
children: ReactNode
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export const Layout = (props: LayoutProps) => {
|
|
14
|
+
const { children } = props
|
|
15
|
+
const { isEmbedded } = useDisplay()
|
|
16
|
+
|
|
17
|
+
return (
|
|
18
|
+
<div className='flex h-screen flex-col overflow-hidden bg-slate-50 text-slate-950 transition-colors dark:bg-[#14171c] dark:text-[#e8edf5]'>
|
|
19
|
+
<LayoutHeaderProvider>
|
|
20
|
+
{isEmbedded ? null : <LayoutHeader />}
|
|
21
|
+
<main className='flex-1 overflow-auto'>
|
|
22
|
+
{children}
|
|
23
|
+
</main>
|
|
24
|
+
</LayoutHeaderProvider>
|
|
25
|
+
</div>
|
|
26
|
+
)
|
|
27
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { type ReactNode, useState } from 'react'
|
|
4
|
+
import { QueryClientProvider } from '@tanstack/react-query'
|
|
5
|
+
|
|
6
|
+
import { api, createApiClient, getQueryClient } from '@/utils'
|
|
7
|
+
|
|
8
|
+
export interface TRPCReactProviderProps {
|
|
9
|
+
children: ReactNode
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export const TRPCReactProvider = (props: TRPCReactProviderProps) => {
|
|
13
|
+
const { children } = props
|
|
14
|
+
|
|
15
|
+
const [apiClient] = useState(createApiClient)
|
|
16
|
+
const [queryClient] = useState(getQueryClient)
|
|
17
|
+
|
|
18
|
+
return (
|
|
19
|
+
<QueryClientProvider client={queryClient}>
|
|
20
|
+
<api.Provider client={apiClient} queryClient={queryClient}>
|
|
21
|
+
{children}
|
|
22
|
+
</api.Provider>
|
|
23
|
+
</QueryClientProvider>
|
|
24
|
+
)
|
|
25
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { type ReactNode } from 'react'
|
|
4
|
+
|
|
5
|
+
import { App, AntdRegistry } from '@/components'
|
|
6
|
+
|
|
7
|
+
import { TRPCReactProvider } from './TRPCReactProvider'
|
|
8
|
+
import { DisplayProvider } from '../display'
|
|
9
|
+
import { ThemeProvider } from '../theme/ThemeProvider'
|
|
10
|
+
|
|
11
|
+
export interface ProvidersProps {
|
|
12
|
+
children: ReactNode
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const Providers = (props: ProvidersProps) => {
|
|
16
|
+
const { children } = props
|
|
17
|
+
|
|
18
|
+
return (
|
|
19
|
+
<TRPCReactProvider>
|
|
20
|
+
<DisplayProvider>
|
|
21
|
+
<AntdRegistry>
|
|
22
|
+
<ThemeProvider>
|
|
23
|
+
<App>
|
|
24
|
+
{children}
|
|
25
|
+
</App>
|
|
26
|
+
</ThemeProvider>
|
|
27
|
+
</AntdRegistry>
|
|
28
|
+
</DisplayProvider>
|
|
29
|
+
</TRPCReactProvider>
|
|
30
|
+
)
|
|
31
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { Suspense, useMemo } from 'react'
|
|
4
|
+
import { useSearchParams } from 'next/navigation'
|
|
5
|
+
|
|
6
|
+
import { defaultDisplay } from './consts'
|
|
7
|
+
import { DisplayContext } from './context'
|
|
8
|
+
import { type DisplayProviderProps } from './types'
|
|
9
|
+
import { getDisplay } from './utils'
|
|
10
|
+
|
|
11
|
+
const DisplayFallbackProvider = (props: DisplayProviderProps) => {
|
|
12
|
+
const { children } = props
|
|
13
|
+
|
|
14
|
+
return (
|
|
15
|
+
<DisplayContext.Provider value={defaultDisplay}>
|
|
16
|
+
{children}
|
|
17
|
+
</DisplayContext.Provider>
|
|
18
|
+
)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const DisplaySearchParamsProvider = (props: DisplayProviderProps) => {
|
|
22
|
+
const { children } = props
|
|
23
|
+
const searchParams = useSearchParams()
|
|
24
|
+
|
|
25
|
+
const display = useMemo(() => getDisplay(searchParams), [searchParams])
|
|
26
|
+
|
|
27
|
+
return (
|
|
28
|
+
<DisplayContext.Provider value={display}>
|
|
29
|
+
{children}
|
|
30
|
+
</DisplayContext.Provider>
|
|
31
|
+
)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export const DisplayProvider = (props: DisplayProviderProps) => {
|
|
35
|
+
const { children } = props
|
|
36
|
+
|
|
37
|
+
return (
|
|
38
|
+
<Suspense fallback={<DisplayFallbackProvider>{children}</DisplayFallbackProvider>}>
|
|
39
|
+
<DisplaySearchParamsProvider>
|
|
40
|
+
{children}
|
|
41
|
+
</DisplaySearchParamsProvider>
|
|
42
|
+
</Suspense>
|
|
43
|
+
)
|
|
44
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { type ThemeMode, themeModes } from '../theme/consts'
|
|
2
|
+
import { urlParamKeys } from './consts'
|
|
3
|
+
import { type Display } from './types'
|
|
4
|
+
|
|
5
|
+
export const getThemeModeFromValue = (value: string | null): ThemeMode | undefined => {
|
|
6
|
+
if (value === themeModes.system || value === themeModes.light || value === themeModes.dark) {
|
|
7
|
+
return value
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
return undefined
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export const getDisplay = (searchParams: URLSearchParams): Display => {
|
|
14
|
+
return {
|
|
15
|
+
isEmbedded: searchParams.get(urlParamKeys.embedded) === '1',
|
|
16
|
+
themeMode: getThemeModeFromValue(searchParams.get(urlParamKeys.theme)),
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { type ReactNode, useEffect, useMemo, useState } from 'react'
|
|
4
|
+
|
|
5
|
+
import { ConfigProvider, zh_CN } from '@/components'
|
|
6
|
+
|
|
7
|
+
import { useProjectLocalStorage } from '@/hooks'
|
|
8
|
+
|
|
9
|
+
import { AppThemeContext, type AppThemeContextValue } from './context'
|
|
10
|
+
import { getThemeModeFromValue, useDisplay } from '../display'
|
|
11
|
+
import { getNextThemeMode, getThemeAlgorithm, getSystemThemeMode } from './utils'
|
|
12
|
+
import { baseThemeToken, darkThemeToken, themeStoragePath, themeModes, type ThemeMode } from './consts'
|
|
13
|
+
|
|
14
|
+
interface AppThemeProviderProps {
|
|
15
|
+
children: ReactNode
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export const ThemeProvider = (props: AppThemeProviderProps) => {
|
|
19
|
+
const { children } = props
|
|
20
|
+
|
|
21
|
+
const { themeMode } = useDisplay()
|
|
22
|
+
const { getValue, setValue } = useProjectLocalStorage('__WZYJS_APP_PACKAGE_NAME__')
|
|
23
|
+
const [storedMode, setStoredMode] = useState<ThemeMode>(themeModes.system)
|
|
24
|
+
const [systemMode, setSystemMode] = useState<ThemeMode>(themeModes.light)
|
|
25
|
+
const [isThemeReady, setIsThemeReady] = useState(false)
|
|
26
|
+
|
|
27
|
+
const mode = themeMode ?? storedMode
|
|
28
|
+
const resolvedMode: ThemeMode = mode === themeModes.system ? systemMode : mode
|
|
29
|
+
|
|
30
|
+
useEffect(() => {
|
|
31
|
+
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)')
|
|
32
|
+
const updateSystemMode = () => setSystemMode(getSystemThemeMode())
|
|
33
|
+
|
|
34
|
+
updateSystemMode()
|
|
35
|
+
|
|
36
|
+
const savedMode = getValue<ThemeMode>(themeStoragePath)
|
|
37
|
+
setStoredMode(getThemeModeFromValue(savedMode ?? null) ?? themeModes.system)
|
|
38
|
+
|
|
39
|
+
setIsThemeReady(true)
|
|
40
|
+
|
|
41
|
+
mediaQuery.addEventListener('change', updateSystemMode)
|
|
42
|
+
|
|
43
|
+
return () => {
|
|
44
|
+
mediaQuery.removeEventListener('change', updateSystemMode)
|
|
45
|
+
}
|
|
46
|
+
}, [getValue])
|
|
47
|
+
|
|
48
|
+
useEffect(() => {
|
|
49
|
+
if (!isThemeReady) {
|
|
50
|
+
return
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
setValue(themeStoragePath, storedMode)
|
|
54
|
+
document.documentElement.classList.toggle(themeModes.dark, resolvedMode === themeModes.dark)
|
|
55
|
+
document.documentElement.style.colorScheme = resolvedMode
|
|
56
|
+
}, [isThemeReady, resolvedMode, setValue, storedMode])
|
|
57
|
+
|
|
58
|
+
const themeValue = useMemo<AppThemeContextValue>(() => ({
|
|
59
|
+
mode,
|
|
60
|
+
resolvedMode,
|
|
61
|
+
setMode: setStoredMode,
|
|
62
|
+
toggleMode: () => setStoredMode(getNextThemeMode),
|
|
63
|
+
}), [mode, resolvedMode])
|
|
64
|
+
|
|
65
|
+
return (
|
|
66
|
+
<AppThemeContext.Provider value={themeValue}>
|
|
67
|
+
<ConfigProvider
|
|
68
|
+
locale={zh_CN}
|
|
69
|
+
theme={{
|
|
70
|
+
algorithm: getThemeAlgorithm(resolvedMode),
|
|
71
|
+
token: {
|
|
72
|
+
...baseThemeToken,
|
|
73
|
+
...(resolvedMode === themeModes.dark
|
|
74
|
+
? darkThemeToken
|
|
75
|
+
: {}),
|
|
76
|
+
},
|
|
77
|
+
}}
|
|
78
|
+
>
|
|
79
|
+
{children}
|
|
80
|
+
</ConfigProvider>
|
|
81
|
+
</AppThemeContext.Provider>
|
|
82
|
+
)
|
|
83
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { Button, Dropdown } from '@/components'
|
|
2
|
+
|
|
3
|
+
import { useAppTheme } from './useAppTheme'
|
|
4
|
+
import { type ThemeMode, themeComponent, themeLabel, themeMenuItems } from './consts'
|
|
5
|
+
|
|
6
|
+
export const ThemeToggle = () => {
|
|
7
|
+
const { mode, setMode } = useAppTheme()
|
|
8
|
+
|
|
9
|
+
return (
|
|
10
|
+
<Dropdown
|
|
11
|
+
trigger={['click']}
|
|
12
|
+
placement='bottomRight'
|
|
13
|
+
menu={{
|
|
14
|
+
items: themeMenuItems,
|
|
15
|
+
selectedKeys: [mode],
|
|
16
|
+
onClick: ({ key }) => setMode(key as ThemeMode),
|
|
17
|
+
}}
|
|
18
|
+
>
|
|
19
|
+
<Button
|
|
20
|
+
shape='circle'
|
|
21
|
+
icon={themeComponent[mode]}
|
|
22
|
+
aria-label={`当前主题:${themeLabel[mode]}`}
|
|
23
|
+
/>
|
|
24
|
+
</Dropdown>
|
|
25
|
+
)
|
|
26
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { type ReactNode } from 'react'
|
|
2
|
+
|
|
3
|
+
import { DesktopOutlined, MoonOutlined, SunOutlined, type MenuProps } from '@/components'
|
|
4
|
+
|
|
5
|
+
export const themeStoragePath = 'theme.mode'
|
|
6
|
+
|
|
7
|
+
export const themeModes = {
|
|
8
|
+
system: 'system',
|
|
9
|
+
light: 'light',
|
|
10
|
+
dark: 'dark',
|
|
11
|
+
} as const
|
|
12
|
+
|
|
13
|
+
export type ThemeMode = typeof themeModes[keyof typeof themeModes]
|
|
14
|
+
|
|
15
|
+
export const themeMenuItems: MenuProps['items'] = [
|
|
16
|
+
{
|
|
17
|
+
key: themeModes.system,
|
|
18
|
+
icon: <DesktopOutlined />,
|
|
19
|
+
label: '跟随系统',
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
key: themeModes.light,
|
|
23
|
+
icon: <SunOutlined />,
|
|
24
|
+
label: '明亮模式',
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
key: themeModes.dark,
|
|
28
|
+
icon: <MoonOutlined />,
|
|
29
|
+
label: '暗黑模式',
|
|
30
|
+
},
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
export const themeLabel: Record<ThemeMode, string> = {
|
|
34
|
+
[themeModes.system]: '跟随系统',
|
|
35
|
+
[themeModes.light]: '明亮模式',
|
|
36
|
+
[themeModes.dark]: '暗黑模式',
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export const themeComponent: Record<ThemeMode, ReactNode> = {
|
|
40
|
+
[themeModes.system]: <DesktopOutlined />,
|
|
41
|
+
[themeModes.light]: <SunOutlined />,
|
|
42
|
+
[themeModes.dark]: <MoonOutlined />,
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export const baseThemeToken = {
|
|
46
|
+
borderRadius: 8,
|
|
47
|
+
colorPrimary: '#2563eb',
|
|
48
|
+
fontFamily: 'Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif',
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export const darkThemeToken = {
|
|
52
|
+
colorBgLayout: '#14171c',
|
|
53
|
+
colorBgContainer: '#1b2028',
|
|
54
|
+
colorBgElevated: '#202632',
|
|
55
|
+
colorBorder: '#303846',
|
|
56
|
+
colorFillAlter: '#242b36',
|
|
57
|
+
colorText: '#e8edf5',
|
|
58
|
+
colorTextSecondary: '#aab4c2',
|
|
59
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { createContext } from 'react'
|
|
4
|
+
|
|
5
|
+
import { type ThemeMode } from './consts'
|
|
6
|
+
|
|
7
|
+
export interface AppThemeContextValue {
|
|
8
|
+
mode: ThemeMode
|
|
9
|
+
setMode: (mode: ThemeMode) => void
|
|
10
|
+
toggleMode: () => void
|
|
11
|
+
resolvedMode: ThemeMode
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const AppThemeContext = createContext<AppThemeContextValue | null>(null)
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { useContext } from 'react'
|
|
4
|
+
|
|
5
|
+
import { AppThemeContext } from './context'
|
|
6
|
+
|
|
7
|
+
export const useAppTheme = () => {
|
|
8
|
+
const context = useContext(AppThemeContext)
|
|
9
|
+
|
|
10
|
+
if (!context) {
|
|
11
|
+
throw new Error('useAppTheme must be used within AppThemeProvider')
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
return context
|
|
15
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { theme } from '@/components'
|
|
2
|
+
import { type ThemeMode, themeModes } from './consts'
|
|
3
|
+
|
|
4
|
+
export const getNextThemeMode = (mode: ThemeMode): ThemeMode => {
|
|
5
|
+
if (mode === themeModes.system) {
|
|
6
|
+
return themeModes.light
|
|
7
|
+
}
|
|
8
|
+
return mode === themeModes.light ? themeModes.dark : themeModes.system
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const getThemeAlgorithm = (mode: ThemeMode) => {
|
|
12
|
+
return mode === themeModes.dark ? theme.darkAlgorithm : theme.defaultAlgorithm
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const getSystemThemeMode = () => {
|
|
16
|
+
return window.matchMedia('(prefers-color-scheme: dark)').matches ? themeModes.dark : themeModes.light
|
|
17
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from '@wzyjs/uis/web'
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { z } from 'zod'
|
|
2
|
+
import { createEnv } from '@t3-oss/env-nextjs'
|
|
3
|
+
|
|
4
|
+
export const env = createEnv({
|
|
5
|
+
/**
|
|
6
|
+
* 在此处指定服务器端环境变量模式。
|
|
7
|
+
* 这样可以确保应用程序不会使用无效的环境变量构建。
|
|
8
|
+
*/
|
|
9
|
+
server: {
|
|
10
|
+
NODE_ENV: z.enum(['development', 'test', 'production']).default('development'),
|
|
11
|
+
// 数据库
|
|
12
|
+
DATABASE_URL: z.string().url(),
|
|
13
|
+
},
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* 在此处指定客户端环境变量模式。
|
|
17
|
+
* 这样可以确保应用程序不会使用无效的环境变量构建。
|
|
18
|
+
* 要将它们暴露给客户端,请使用 `NEXT_PUBLIC_` 前缀。
|
|
19
|
+
*/
|
|
20
|
+
client: {},
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* 在这里显式传入运行时环境变量的实际值。
|
|
24
|
+
* `server` / `client` 负责声明校验规则,
|
|
25
|
+
* `runtimeEnv` 负责把 `process.env` 中对应的值提供给 `createEnv`。
|
|
26
|
+
*/
|
|
27
|
+
runtimeEnv: {
|
|
28
|
+
NODE_ENV: process.env.NODE_ENV,
|
|
29
|
+
// 数据库
|
|
30
|
+
DATABASE_URL: process.env.DATABASE_URL,
|
|
31
|
+
},
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* 使用 `SKIP_ENV_VALIDATION` 运行 `build` 或 `dev` 可以跳过环境变量验证。
|
|
35
|
+
* 这在 Docker 构建时特别有用。
|
|
36
|
+
*/
|
|
37
|
+
skipValidation: !!process.env.SKIP_ENV_VALIDATION,
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* 使空字符串被视为未定义。
|
|
41
|
+
* 例如:`SOME_VAR: z.string()` 和 `SOME_VAR=''` 将抛出错误。
|
|
42
|
+
*/
|
|
43
|
+
emptyStringAsUndefined: true,
|
|
44
|
+
})
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from '@wzyjs/hooks/web'
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { PrismaClient } from '@prisma/client'
|
|
2
|
+
|
|
3
|
+
import { env } from '@/env'
|
|
4
|
+
|
|
5
|
+
const createPrismaClient = () => {
|
|
6
|
+
return new PrismaClient({
|
|
7
|
+
log: env.NODE_ENV === 'development' ? ['error', 'warn'] : ['error'],
|
|
8
|
+
})
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const globalForPrisma = globalThis as unknown as {
|
|
12
|
+
prisma: ReturnType<typeof createPrismaClient> | undefined
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const db = globalForPrisma.prisma ?? createPrismaClient()
|
|
16
|
+
|
|
17
|
+
if (env.NODE_ENV !== 'production') {
|
|
18
|
+
globalForPrisma.prisma = db
|
|
19
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { db } from '@/server/db/client'
|
|
2
|
+
|
|
3
|
+
import { enhance } from '../generated/enhancer/enhance'
|
|
4
|
+
|
|
5
|
+
export const createTRPCContext = async (opts?: { req?: Request }) => {
|
|
6
|
+
return {
|
|
7
|
+
// db, 严禁使用这个模块,而应该使用 prisma
|
|
8
|
+
req: opts?.req,
|
|
9
|
+
headers: opts?.req?.headers,
|
|
10
|
+
prisma: enhance(db),
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export type TRPCContext = Awaited<ReturnType<typeof createTRPCContext>>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { apiRouter, type ApiRouter, type ApiRouterInput, type ApiRouterOutput } from './router'
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { initTRPC } from '@trpc/server'
|
|
2
|
+
import superjson from 'superjson'
|
|
3
|
+
import { ZodError } from 'zod'
|
|
4
|
+
import { fromZodError } from 'zod-validation-error'
|
|
5
|
+
|
|
6
|
+
import { type TRPCContext } from './context'
|
|
7
|
+
|
|
8
|
+
const t = initTRPC.context<TRPCContext>().create({
|
|
9
|
+
transformer: superjson,
|
|
10
|
+
errorFormatter({ shape, error }) {
|
|
11
|
+
return {
|
|
12
|
+
...shape,
|
|
13
|
+
data: {
|
|
14
|
+
...shape.data,
|
|
15
|
+
formattedError:
|
|
16
|
+
error.cause instanceof ZodError
|
|
17
|
+
? fromZodError(error.cause).message
|
|
18
|
+
: null,
|
|
19
|
+
},
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
export const createCallerFactory = t.createCallerFactory
|
|
25
|
+
export const createTRPCRouter = t.router
|
|
26
|
+
export const createTRPCProcedure = t.procedure
|
|
27
|
+
export const mergeTRPCRouters = t.mergeRouters
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { inferRouterInputs, inferRouterOutputs } from '@trpc/server'
|
|
2
|
+
|
|
3
|
+
import routes from '../routers'
|
|
4
|
+
|
|
5
|
+
export const apiRouter = routes
|
|
6
|
+
|
|
7
|
+
export type ApiRouter = typeof apiRouter
|
|
8
|
+
|
|
9
|
+
export type ApiRouterInput = inferRouterInputs<ApiRouter>
|
|
10
|
+
|
|
11
|
+
export type ApiRouterOutput = inferRouterOutputs<ApiRouter>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from '@wzyjs/utils/node'
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export type { ApiRouterInput, ApiRouterOutput } from '@/server/trpc'
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import SuperJSON from 'superjson'
|
|
2
|
+
import { QueryClient, defaultShouldDehydrateQuery } from '@tanstack/react-query'
|
|
3
|
+
|
|
4
|
+
let clientQueryClientSingleton: QueryClient | undefined = undefined
|
|
5
|
+
|
|
6
|
+
const createQueryClient = () => {
|
|
7
|
+
return new QueryClient({
|
|
8
|
+
defaultOptions: {
|
|
9
|
+
queries: {
|
|
10
|
+
staleTime: 30 * 1000,
|
|
11
|
+
},
|
|
12
|
+
hydrate: {
|
|
13
|
+
deserializeData: SuperJSON.deserialize,
|
|
14
|
+
},
|
|
15
|
+
dehydrate: {
|
|
16
|
+
serializeData: SuperJSON.serialize,
|
|
17
|
+
shouldDehydrateQuery: (query) => {
|
|
18
|
+
return defaultShouldDehydrateQuery(query) || query.state.status === 'pending'
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
})
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export const getQueryClient = () => {
|
|
26
|
+
if (typeof window === 'undefined') {
|
|
27
|
+
return createQueryClient()
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return (clientQueryClientSingleton ??= createQueryClient())
|
|
31
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import SuperJSON from 'superjson'
|
|
4
|
+
|
|
5
|
+
import { createTRPCReact } from '@trpc/react-query'
|
|
6
|
+
import { httpBatchLink } from '@trpc/client'
|
|
7
|
+
|
|
8
|
+
import { type ApiRouter } from '@/server/trpc'
|
|
9
|
+
import { errorLink, getBaseUrl } from './utils'
|
|
10
|
+
|
|
11
|
+
export const api = createTRPCReact<ApiRouter>()
|
|
12
|
+
|
|
13
|
+
export const createApiClient = () => {
|
|
14
|
+
return api.createClient({
|
|
15
|
+
links: [
|
|
16
|
+
errorLink,
|
|
17
|
+
httpBatchLink({
|
|
18
|
+
transformer: SuperJSON,
|
|
19
|
+
url: getBaseUrl() + '/api/trpc',
|
|
20
|
+
}),
|
|
21
|
+
],
|
|
22
|
+
})
|
|
23
|
+
}
|