@wzyjs/cli 0.3.32 → 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.
Files changed (106) hide show
  1. package/dist/index.js +350 -35
  2. package/package.json +6 -4
  3. package/template/web/base/Dockerfile +37 -0
  4. package/template/web/base/_env +2 -0
  5. package/template/web/base/eslint.config.js +94 -0
  6. package/template/web/base/next.config.js +29 -0
  7. package/template/web/base/package.json +53 -0
  8. package/template/web/base/postcss.config.js +5 -0
  9. package/template/web/base/prisma/models/demo/DemoItem.zmodel +8 -0
  10. package/template/web/base/prisma/schema.prisma +29 -0
  11. package/template/web/base/prisma/schema.zmodel +1 -0
  12. package/template/web/base/src/app/api/trpc/[trpc]/route.ts +24 -0
  13. package/template/web/base/src/app/auth/disabled/page.tsx +29 -0
  14. package/template/web/base/src/app/auth/error/page.tsx +47 -0
  15. package/template/web/base/src/app/auth/signin/page.tsx +53 -0
  16. package/template/web/base/src/app/auth/unauthorized/page.tsx +60 -0
  17. package/template/web/base/src/app/demo/page.tsx +59 -0
  18. package/template/web/base/src/app/layout.tsx +32 -0
  19. package/template/web/base/src/app/not-found.tsx +21 -0
  20. package/template/web/base/src/app/page.tsx +92 -0
  21. package/template/web/base/src/components/base/Layout/Header/context.tsx +56 -0
  22. package/template/web/base/src/components/base/Layout/Header/index.tsx +32 -0
  23. package/template/web/base/src/components/base/Layout/index.tsx +27 -0
  24. package/template/web/base/src/components/base/Providers/TRPCReactProvider.tsx +25 -0
  25. package/template/web/base/src/components/base/Providers/index.tsx +31 -0
  26. package/template/web/base/src/components/base/display/DisplayProvider.tsx +44 -0
  27. package/template/web/base/src/components/base/display/consts.ts +10 -0
  28. package/template/web/base/src/components/base/display/context.ts +6 -0
  29. package/template/web/base/src/components/base/display/index.ts +4 -0
  30. package/template/web/base/src/components/base/display/types.ts +12 -0
  31. package/template/web/base/src/components/base/display/useDisplay.ts +9 -0
  32. package/template/web/base/src/components/base/display/utils.ts +18 -0
  33. package/template/web/base/src/components/base/theme/ThemeProvider.tsx +83 -0
  34. package/template/web/base/src/components/base/theme/ThemeToggle.tsx +26 -0
  35. package/template/web/base/src/components/base/theme/consts.tsx +59 -0
  36. package/template/web/base/src/components/base/theme/context.ts +14 -0
  37. package/template/web/base/src/components/base/theme/useAppTheme.ts +15 -0
  38. package/template/web/base/src/components/base/theme/utils.ts +17 -0
  39. package/template/web/base/src/components/index.ts +1 -0
  40. package/template/web/base/src/consts/index.ts +6 -0
  41. package/template/web/base/src/enums/index.ts +1 -0
  42. package/template/web/base/src/env.js +44 -0
  43. package/template/web/base/src/hooks/index.ts +1 -0
  44. package/template/web/base/src/server/db/client.ts +19 -0
  45. package/template/web/base/src/server/routers/index.ts +3 -0
  46. package/template/web/base/src/server/trpc/context.ts +14 -0
  47. package/template/web/base/src/server/trpc/index.ts +1 -0
  48. package/template/web/base/src/server/trpc/init.ts +27 -0
  49. package/template/web/base/src/server/trpc/procedures.ts +5 -0
  50. package/template/web/base/src/server/trpc/router.ts +11 -0
  51. package/template/web/base/src/server/utils/index.ts +1 -0
  52. package/template/web/base/src/styles/globals.css +3 -0
  53. package/template/web/base/src/types/index.ts +1 -0
  54. package/template/web/base/src/utils/index.ts +4 -0
  55. package/template/web/base/src/utils/query-client/index.ts +31 -0
  56. package/template/web/base/src/utils/trpc/index.ts +23 -0
  57. package/template/web/base/src/utils/trpc/utils.ts +61 -0
  58. package/template/web/base/tailwind.config.cjs +7 -0
  59. package/template/web/base/tsconfig.json +46 -0
  60. package/template/web/google/_env +8 -0
  61. package/template/web/google/package.json +55 -0
  62. package/template/web/google/prisma/models/auth/Account.zmodel +26 -0
  63. package/template/web/google/prisma/models/auth/Session.zmodel +17 -0
  64. package/template/web/google/prisma/models/auth/User.zmodel +22 -0
  65. package/template/web/google/prisma/schema.zmodel +2 -0
  66. package/template/web/google/src/app/api/auth/[...nextauth]/route.ts +7 -0
  67. package/template/web/google/src/app/auth/error/page.tsx +56 -0
  68. package/template/web/google/src/app/auth/signin/page.tsx +83 -0
  69. package/template/web/google/src/app/layout.tsx +35 -0
  70. package/template/web/google/src/components/base/AuthGuard/Loading.tsx +19 -0
  71. package/template/web/google/src/components/base/AuthGuard/index.tsx +45 -0
  72. package/template/web/google/src/components/base/Layout/Header/UserAuthStatus.tsx +93 -0
  73. package/template/web/google/src/components/base/Layout/Header/index.tsx +34 -0
  74. package/template/web/google/src/components/base/Providers/index.tsx +34 -0
  75. package/template/web/google/src/env.js +52 -0
  76. package/template/web/google/src/server/auth/next-auth/adapter.ts +77 -0
  77. package/template/web/google/src/server/auth/next-auth/options.ts +53 -0
  78. package/template/web/google/src/server/trpc/context.ts +21 -0
  79. package/template/web/google/src/server/trpc/procedures.ts +16 -0
  80. package/template/web/middle/_env +10 -0
  81. package/template/web/middle/package.json +55 -0
  82. package/template/web/middle/src/app/api/auth/[...nextauth]/route.ts +7 -0
  83. package/template/web/middle/src/app/auth/disabled/page.tsx +71 -0
  84. package/template/web/middle/src/app/auth/error/page.tsx +56 -0
  85. package/template/web/middle/src/app/auth/signin/page.tsx +91 -0
  86. package/template/web/middle/src/app/auth/unauthorized/page.tsx +88 -0
  87. package/template/web/middle/src/app/layout.tsx +35 -0
  88. package/template/web/middle/src/app/middle/page.tsx +10 -0
  89. package/template/web/middle/src/app/page.tsx +20 -0
  90. package/template/web/middle/src/components/base/AuthGuard/Loading.tsx +19 -0
  91. package/template/web/middle/src/components/base/AuthGuard/index.tsx +45 -0
  92. package/template/web/middle/src/components/base/Layout/Header/UserAuthStatus.tsx +93 -0
  93. package/template/web/middle/src/components/base/Layout/Header/index.tsx +34 -0
  94. package/template/web/middle/src/components/base/Layout/Sidebar/index.tsx +88 -0
  95. package/template/web/middle/src/components/base/Layout/index.tsx +35 -0
  96. package/template/web/middle/src/components/base/Providers/MiddleProvider.tsx +33 -0
  97. package/template/web/middle/src/components/base/Providers/index.tsx +37 -0
  98. package/template/web/middle/src/env.js +57 -0
  99. package/template/web/middle/src/server/auth/next-auth/options.ts +26 -0
  100. package/template/web/middle/src/server/trpc/context.ts +22 -0
  101. package/template/web/middle/src/server/trpc/procedures.ts +16 -0
  102. package/template/web/middle/src/server/utils/index.ts +2 -0
  103. package/template/web/middle/src/server/utils/middle.ts +34 -0
  104. package/template/web/middle/src/types/index.ts +3 -0
  105. package/template/web/middle/src/utils/index.ts +5 -0
  106. 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,10 @@
1
+ import { type Display } from './types'
2
+
3
+ export const urlParamKeys = {
4
+ embedded: 'embedded',
5
+ theme: 'theme',
6
+ } as const
7
+
8
+ export const defaultDisplay: Display = {
9
+ isEmbedded: false,
10
+ }
@@ -0,0 +1,6 @@
1
+ import { createContext } from 'react'
2
+
3
+ import { defaultDisplay } from './consts'
4
+ import { type Display } from './types'
5
+
6
+ export const DisplayContext = createContext<Display>(defaultDisplay)
@@ -0,0 +1,4 @@
1
+ export { DisplayProvider } from './DisplayProvider'
2
+ export { useDisplay } from './useDisplay'
3
+ export { getDisplay, getThemeModeFromValue } from './utils'
4
+ export type { Display } from './types'
@@ -0,0 +1,12 @@
1
+ import { type ReactNode } from 'react'
2
+
3
+ import { type ThemeMode } from '../theme/consts'
4
+
5
+ export interface Display {
6
+ isEmbedded: boolean
7
+ themeMode?: ThemeMode
8
+ }
9
+
10
+ export interface DisplayProviderProps {
11
+ children: ReactNode
12
+ }
@@ -0,0 +1,9 @@
1
+ 'use client'
2
+
3
+ import { useContext } from 'react'
4
+
5
+ import { DisplayContext } from './context'
6
+
7
+ export const useDisplay = () => {
8
+ return useContext(DisplayContext)
9
+ }
@@ -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,6 @@
1
+ export const authExemptPaths = [
2
+ '/auth/signin',
3
+ '/auth/unauthorized',
4
+ '/auth/disabled',
5
+ '/auth/error',
6
+ ]
@@ -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,3 @@
1
+ import { createRouter } from '../generated/trpc/routers'
2
+
3
+ export default createRouter()
@@ -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,5 @@
1
+ import { createTRPCProcedure } from './init'
2
+
3
+ export const publicProcedure = createTRPCProcedure
4
+
5
+ export const procedure = createTRPCProcedure
@@ -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,3 @@
1
+ @tailwind base;
2
+ @tailwind components;
3
+ @tailwind utilities;
@@ -0,0 +1 @@
1
+ export type { ApiRouterInput, ApiRouterOutput } from '@/server/trpc'
@@ -0,0 +1,4 @@
1
+ export * from '@wzyjs/utils/web'
2
+
3
+ export { getQueryClient } from './query-client'
4
+ export { api, createApiClient } from './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
+ }