@windrun-huaiin/diaomao 30.0.0 → 31.0.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/.env.local.txt +1 -1
- package/messages/en.json +13 -0
- package/package.json +9 -9
- package/src/app/[locale]/(content)/blog/[[...slug]]/page.tsx +1 -5
- package/src/app/[locale]/(content)/blog/layout.tsx +4 -3
- package/src/app/[locale]/(content)/legal/[[...slug]]/page.tsx +2 -6
- package/src/app/[locale]/(content)/legal/layout.tsx +4 -3
- package/src/app/[locale]/(home)/[...catchAll]/page.tsx +4 -7
- package/src/app/[locale]/(home)/layout.heavy.tsx +7 -2
- package/src/app/[locale]/(home)/layout.tsx +2 -1
- package/src/app/[locale]/layout.nav.tsx +3 -3
- package/src/app/[locale]/layout.tsx +2 -3
- package/src/app/[locale]/not-found.tsx +6 -0
- package/src/lib/appConfig.ts +1 -0
- package/src/lib/fonts.ts +1 -1
- package/src/mdx/blog/index.mdx +1 -1
- package/src/mdx/blog/ioc.mdx +1 -1
- package/src/proxy.ts +15 -14
- package/src/components/credit-popover-client.tsx +0 -72
- package/src/components/credit-popover.tsx +0 -136
package/.env.local.txt
CHANGED
|
@@ -30,7 +30,7 @@ NEXT_PUBLIC_CLERK_SIGN_UP_FALLBACK_REDIRECT_URL=/
|
|
|
30
30
|
NEXT_PUBLIC_CLERK_WAITLIST_URL=/waitlist
|
|
31
31
|
|
|
32
32
|
## Media loading timeout
|
|
33
|
-
NEXT_PUBLIC_DELAYED_IMG_SECONDS=
|
|
33
|
+
NEXT_PUBLIC_DELAYED_IMG_SECONDS=1
|
|
34
34
|
NEXT_PUBLIC_SUNO_EMBED_TIMEOUT_SECONDS=20
|
|
35
35
|
|
|
36
36
|
## OpenRouter·AI base config
|
package/messages/en.json
CHANGED
|
@@ -109,6 +109,11 @@
|
|
|
109
109
|
{
|
|
110
110
|
"key": "P2",
|
|
111
111
|
"title": "Pro",
|
|
112
|
+
"animeTone": "cool",
|
|
113
|
+
"strictDiffAnime": {
|
|
114
|
+
"monthly": "theme",
|
|
115
|
+
"yearly": "rainbow"
|
|
116
|
+
},
|
|
112
117
|
"showBillingSubTitle": true,
|
|
113
118
|
"features": [
|
|
114
119
|
{
|
|
@@ -126,6 +131,11 @@
|
|
|
126
131
|
{
|
|
127
132
|
"key": "U3",
|
|
128
133
|
"title": "Ultra",
|
|
134
|
+
"animeTone": "rainbow",
|
|
135
|
+
"strictDiffAnime": {
|
|
136
|
+
"monthly": "rainbow",
|
|
137
|
+
"yearly": "cool"
|
|
138
|
+
},
|
|
129
139
|
"showBillingSubTitle": true,
|
|
130
140
|
"titleTags": ["Most Popular"],
|
|
131
141
|
"features": [
|
|
@@ -152,6 +162,7 @@
|
|
|
152
162
|
{
|
|
153
163
|
"key": "F1",
|
|
154
164
|
"title": "Starter",
|
|
165
|
+
"animeTone": "mono",
|
|
155
166
|
"subtitle": "25 Credits",
|
|
156
167
|
"showBillingSubTitle": true,
|
|
157
168
|
"features": [
|
|
@@ -164,6 +175,7 @@
|
|
|
164
175
|
{
|
|
165
176
|
"key": "P2",
|
|
166
177
|
"title": "Popular",
|
|
178
|
+
"animeTone": "theme",
|
|
167
179
|
"subtitle": "150 Credits",
|
|
168
180
|
"showBillingSubTitle": true,
|
|
169
181
|
"titleTags": ["Preferred"],
|
|
@@ -178,6 +190,7 @@
|
|
|
178
190
|
{
|
|
179
191
|
"key": "U3",
|
|
180
192
|
"title": "Power",
|
|
193
|
+
"animeTone": "rainbow",
|
|
181
194
|
"subtitle": "350 Credits",
|
|
182
195
|
"showBillingSubTitle": true,
|
|
183
196
|
"features": [
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@windrun-huaiin/diaomao",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "31.0.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -15,14 +15,14 @@
|
|
|
15
15
|
"react"
|
|
16
16
|
],
|
|
17
17
|
"dependencies": {
|
|
18
|
-
"@clerk/nextjs": "^7.
|
|
18
|
+
"@clerk/nextjs": "^7.3.3",
|
|
19
19
|
"@types/mdx": "^2.0.13",
|
|
20
|
-
"@windrun-huaiin/backend-core": "
|
|
21
|
-
"@windrun-huaiin/base-ui": "
|
|
22
|
-
"@windrun-huaiin/contracts": "
|
|
23
|
-
"@windrun-huaiin/fumadocs-local-md": "
|
|
24
|
-
"@windrun-huaiin/lib": "
|
|
25
|
-
"@windrun-huaiin/third-ui": "
|
|
20
|
+
"@windrun-huaiin/backend-core": "31.0.0",
|
|
21
|
+
"@windrun-huaiin/base-ui": "31.0.0",
|
|
22
|
+
"@windrun-huaiin/contracts": "31.0.0",
|
|
23
|
+
"@windrun-huaiin/fumadocs-local-md": "31.0.0",
|
|
24
|
+
"@windrun-huaiin/lib": "31.0.0",
|
|
25
|
+
"@windrun-huaiin/third-ui": "31.0.1",
|
|
26
26
|
"clsx": "^2.1.1",
|
|
27
27
|
"lucide-react": "^0.577.0",
|
|
28
28
|
"next": "16.1.6",
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
"@types/react": "^19.2.14",
|
|
45
45
|
"@types/react-dom": "^19.2.3",
|
|
46
46
|
"@typescript-eslint/parser": "^8.56.1",
|
|
47
|
-
"@windrun-huaiin/dev-scripts": "^
|
|
47
|
+
"@windrun-huaiin/dev-scripts": "^31.0.0",
|
|
48
48
|
"baseline-browser-mapping": "^2.10.0",
|
|
49
49
|
"dotenv": "^17.4.2",
|
|
50
50
|
"eslint": "^9.39.1",
|
|
@@ -1,20 +1,16 @@
|
|
|
1
1
|
import { appConfig } from '@/lib/appConfig';
|
|
2
2
|
import { siteDocs } from '@/lib/site-docs';
|
|
3
|
-
import { NotFoundPage } from '@windrun-huaiin/base-ui/components';
|
|
4
3
|
import { createFumaPage } from '@windrun-huaiin/third-ui/fuma/server/page-generator';
|
|
5
|
-
import { SiteIcon } from '@/lib/site-config';
|
|
6
4
|
import { LLMCopyButton } from '@windrun-huaiin/third-ui/fuma/mdx';
|
|
7
5
|
|
|
8
6
|
const sourceKey = 'blog';
|
|
9
7
|
const { Page, generateStaticParams, generateMetadata } = createFumaPage({
|
|
10
8
|
sourceKey: sourceKey,
|
|
11
|
-
mdxContentSource: () => siteDocs.getContentSource(
|
|
9
|
+
mdxContentSource: () => siteDocs.getContentSource(sourceKey),
|
|
12
10
|
getMDXComponents: siteDocs.getMDXComponents,
|
|
13
11
|
mdxSourceDir: appConfig.mdxSourceDir[sourceKey],
|
|
14
12
|
githubBaseUrl: appConfig.githubBaseUrl,
|
|
15
13
|
copyButtonComponent: <LLMCopyButton />,
|
|
16
|
-
siteIcon: <SiteIcon />,
|
|
17
|
-
FallbackPage: NotFoundPage,
|
|
18
14
|
showBreadcrumb: false,
|
|
19
15
|
showTableOfContent: true,
|
|
20
16
|
showTableOfContentPopover: false,
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import type { ReactNode } from 'react';
|
|
2
2
|
import { baseOptions } from '@/app/[locale]/layout.config';
|
|
3
3
|
import { levelNavLinks, primaryNavLinks } from '@/app/[locale]/layout.nav';
|
|
4
|
-
import { showBanner, localePrefixAsNeeded, defaultLocale } from '@/lib/appConfig';
|
|
4
|
+
import { showBanner, localePrefixAsNeeded, defaultLocale, github } from '@/lib/appConfig';
|
|
5
|
+
import { i18n } from '@/lib/i18n-base';
|
|
5
6
|
import { siteDocs } from '@/lib/site-docs';
|
|
6
7
|
import { SiteDocsLayout } from '@windrun-huaiin/third-ui/fuma/base/site-docs-layout';
|
|
7
8
|
import { SiteHomeLayout, type SiteHomeLayoutConfig } from '@windrun-huaiin/third-ui/fuma/base/site-home-layout';
|
|
8
9
|
import { fingerprintConfig } from '@windrun-huaiin/backend-core/config/fingerprint';
|
|
9
10
|
import { FingerprintProvider } from '@windrun-huaiin/third-ui/fingerprint';
|
|
10
|
-
import { appConfig } from '@/lib/appConfig';
|
|
11
11
|
|
|
12
12
|
async function contentOptions(locale: string): Promise<SiteHomeLayoutConfig> {
|
|
13
13
|
return {
|
|
@@ -31,7 +31,8 @@ export default async function Layout({
|
|
|
31
31
|
const contentLayoutOptions = await contentOptions(locale);
|
|
32
32
|
const homeLayoutOptions: SiteHomeLayoutConfig = {
|
|
33
33
|
...contentLayoutOptions,
|
|
34
|
-
|
|
34
|
+
i18n,
|
|
35
|
+
githubUrl: github,
|
|
35
36
|
searchToggle: {
|
|
36
37
|
enabled: false,
|
|
37
38
|
},
|
|
@@ -1,23 +1,19 @@
|
|
|
1
1
|
import { appConfig } from '@/lib/appConfig';
|
|
2
2
|
import { siteDocs } from '@/lib/site-docs';
|
|
3
|
-
import { SiteIcon } from '@/lib/site-config';
|
|
4
|
-
import { NotFoundPage } from '@windrun-huaiin/base-ui/components';
|
|
5
3
|
import { createFumaPage } from '@windrun-huaiin/third-ui/fuma/server/page-generator';
|
|
6
4
|
|
|
7
5
|
const sourceKey = 'legal';
|
|
8
6
|
const { Page, generateStaticParams, generateMetadata } = createFumaPage({
|
|
9
7
|
sourceKey: sourceKey,
|
|
10
|
-
mdxContentSource: () => siteDocs.getContentSource(
|
|
8
|
+
mdxContentSource: () => siteDocs.getContentSource(sourceKey),
|
|
11
9
|
getMDXComponents: siteDocs.getMDXComponents,
|
|
12
10
|
mdxSourceDir: appConfig.mdxSourceDir[sourceKey],
|
|
13
11
|
githubBaseUrl: appConfig.githubBaseUrl,
|
|
14
|
-
siteIcon: <SiteIcon />,
|
|
15
|
-
FallbackPage: NotFoundPage,
|
|
16
12
|
supportedLocales: appConfig.i18n.locales as string[],
|
|
17
13
|
showBreadcrumb: false,
|
|
18
14
|
showTableOfContent: true,
|
|
19
15
|
showTableOfContentPopover: false,
|
|
20
|
-
tocRenderMode: 'fumadocs-
|
|
16
|
+
tocRenderMode: 'fumadocs-normal'
|
|
21
17
|
});
|
|
22
18
|
|
|
23
19
|
export default Page;
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import type { ReactNode } from 'react';
|
|
2
2
|
import { baseOptions } from '@/app/[locale]/layout.config';
|
|
3
3
|
import { levelNavLinks, primaryNavLinks } from '@/app/[locale]/layout.nav';
|
|
4
|
-
import { showBanner, localePrefixAsNeeded, defaultLocale } from '@/lib/appConfig';
|
|
4
|
+
import { showBanner, localePrefixAsNeeded, defaultLocale, github } from '@/lib/appConfig';
|
|
5
|
+
import { i18n } from '@/lib/i18n-base';
|
|
5
6
|
import { siteDocs } from '@/lib/site-docs';
|
|
6
7
|
import { SiteDocsLayout } from '@windrun-huaiin/third-ui/fuma/base/site-docs-layout';
|
|
7
8
|
import { SiteHomeLayout, type SiteHomeLayoutConfig } from '@windrun-huaiin/third-ui/fuma/base/site-home-layout';
|
|
8
|
-
import { appConfig } from '@/lib/appConfig';
|
|
9
9
|
|
|
10
10
|
async function contentOptions(locale: string): Promise<SiteHomeLayoutConfig> {
|
|
11
11
|
return {
|
|
@@ -29,7 +29,8 @@ export default async function Layout({
|
|
|
29
29
|
const contentLayoutOptions = await contentOptions(locale);
|
|
30
30
|
const homeLayoutOptions: SiteHomeLayoutConfig = {
|
|
31
31
|
...contentLayoutOptions,
|
|
32
|
-
|
|
32
|
+
i18n,
|
|
33
|
+
githubUrl: github,
|
|
33
34
|
searchToggle: {
|
|
34
35
|
enabled: false,
|
|
35
36
|
},
|
|
@@ -1,8 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { SiteIcon } from '@/lib/site-config';
|
|
1
|
+
import { notFound } from 'next/navigation';
|
|
3
2
|
|
|
4
|
-
export default function
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
);
|
|
8
|
-
}
|
|
3
|
+
export default function CatchAllPage() {
|
|
4
|
+
notFound();
|
|
5
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { CreditOverviewNavClient } from '@windrun-huaiin/third-ui/main/credit';
|
|
2
2
|
import { appConfig } from '@/lib/appConfig';
|
|
3
3
|
import { ClerkUser } from '@windrun-huaiin/third-ui/clerk/server';
|
|
4
4
|
import type { SiteNavItemConfig } from '@windrun-huaiin/third-ui/fuma/base/site-layout-shared';
|
|
@@ -9,7 +9,12 @@ export async function homeHeavyItems(locale: string): Promise<SiteNavItemConfig[
|
|
|
9
9
|
type: 'custom',
|
|
10
10
|
secondary: true,
|
|
11
11
|
mobilePinned: true,
|
|
12
|
-
children:
|
|
12
|
+
children: (
|
|
13
|
+
<CreditOverviewNavClient
|
|
14
|
+
locale={locale}
|
|
15
|
+
endpoint="/api/user/credit-overview"
|
|
16
|
+
/>
|
|
17
|
+
),
|
|
13
18
|
},
|
|
14
19
|
{
|
|
15
20
|
type: 'custom',
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { baseOptions } from '@/app/[locale]/layout.config';
|
|
2
2
|
import { levelNavLinks, primaryNavLinks } from '@/app/[locale]/layout.nav';
|
|
3
3
|
import { homeHeavyItems } from './layout.heavy';
|
|
4
|
-
import { showBanner, localePrefixAsNeeded, defaultLocale } from '@/lib/appConfig';
|
|
4
|
+
import { showBanner, localePrefixAsNeeded, defaultLocale, github } from '@/lib/appConfig';
|
|
5
5
|
import { i18n } from '@/lib/i18n-base';
|
|
6
6
|
import { fingerprintConfig } from '@windrun-huaiin/backend-core/config/fingerprint';
|
|
7
7
|
import { FingerprintProvider } from '@windrun-huaiin/third-ui/fingerprint';
|
|
@@ -31,6 +31,7 @@ export default async function Layout({
|
|
|
31
31
|
const homeLayoutOptions: SiteHomeLayoutConfig = {
|
|
32
32
|
...customeOptions,
|
|
33
33
|
i18n,
|
|
34
|
+
githubUrl: github,
|
|
34
35
|
searchToggle: {
|
|
35
36
|
enabled: false,
|
|
36
37
|
},
|
|
@@ -67,7 +67,7 @@ export async function levelNavLinks(locale: string): Promise<SiteNavItemConfig[]
|
|
|
67
67
|
const blogsLinks: SiteMenuLeafConfig[] = [
|
|
68
68
|
{
|
|
69
69
|
text: 'async-architecture',
|
|
70
|
-
description: '
|
|
70
|
+
description: 'Async handler',
|
|
71
71
|
path: '/blog/async-architecture',
|
|
72
72
|
prefetch: false,
|
|
73
73
|
icon: <T3PIcon />,
|
|
@@ -75,7 +75,7 @@ export async function levelNavLinks(locale: string): Promise<SiteNavItemConfig[]
|
|
|
75
75
|
},
|
|
76
76
|
{
|
|
77
77
|
text: 'Config Sheet',
|
|
78
|
-
description: '
|
|
78
|
+
description: 'Quickstart config',
|
|
79
79
|
path: '/blog/cheatsheet',
|
|
80
80
|
prefetch: false,
|
|
81
81
|
icon: <SettingsIcon />,
|
|
@@ -83,7 +83,7 @@ export async function levelNavLinks(locale: string): Promise<SiteNavItemConfig[]
|
|
|
83
83
|
},
|
|
84
84
|
{
|
|
85
85
|
text: 'IOC',
|
|
86
|
-
description: 'IOC
|
|
86
|
+
description: 'IOC Analysis',
|
|
87
87
|
path: '/blog/ioc',
|
|
88
88
|
prefetch: false,
|
|
89
89
|
icon: <ChartColumnStackedIcon />,
|
|
@@ -13,7 +13,6 @@ import React from 'react';
|
|
|
13
13
|
|
|
14
14
|
export const dynamic = 'force-dynamic'
|
|
15
15
|
|
|
16
|
-
// 网站元数据
|
|
17
16
|
export async function generateMetadata({
|
|
18
17
|
params: paramsPromise
|
|
19
18
|
}: {
|
|
@@ -31,12 +30,12 @@ export async function generateMetadata({
|
|
|
31
30
|
|
|
32
31
|
export default async function RootLayout({
|
|
33
32
|
children,
|
|
34
|
-
params: paramsPromise
|
|
33
|
+
params: paramsPromise
|
|
35
34
|
}: {
|
|
36
35
|
children: React.ReactNode
|
|
37
36
|
params: Promise<{ locale: string }>
|
|
38
37
|
}) {
|
|
39
|
-
const { locale } = await paramsPromise;
|
|
38
|
+
const { locale } = await paramsPromise;
|
|
40
39
|
setRequestLocale(locale);
|
|
41
40
|
const messages = await getMessages();
|
|
42
41
|
const fumaTranslations = await getFumaTranslations(locale);
|
package/src/lib/appConfig.ts
CHANGED
|
@@ -9,6 +9,7 @@ export const appConfig = {
|
|
|
9
9
|
export const { isSupportedLocale, getValidLocale, generatedLocales } = createI18nHelpers(appConfig.i18n);
|
|
10
10
|
|
|
11
11
|
export const { localePrefixAsNeeded, defaultLocale } = appConfig.i18n;
|
|
12
|
+
export const github = appConfig.github;
|
|
12
13
|
|
|
13
14
|
// export shortcuts
|
|
14
15
|
export const { iconColor, watermark, showBanner, clerkPageBanner, clerkAuthInModal, placeHolderImage } = appConfig.shortcuts;
|
package/src/lib/fonts.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import localFont from 'next/font/local';
|
|
2
2
|
|
|
3
|
-
//
|
|
3
|
+
// Just use local font,no more remote Google-font
|
|
4
4
|
export const montserrat = localFont({
|
|
5
5
|
src: [
|
|
6
6
|
{ path: '../../public/asserts/Montserrat-Regular.otf', weight: '400', style: 'normal' },
|
package/src/mdx/blog/index.mdx
CHANGED
package/src/mdx/blog/ioc.mdx
CHANGED
package/src/proxy.ts
CHANGED
|
@@ -12,7 +12,7 @@ const intlMiddleware = createMiddleware({
|
|
|
12
12
|
localeDetection: false
|
|
13
13
|
});
|
|
14
14
|
|
|
15
|
-
//
|
|
15
|
+
// Page routes that require authentication.
|
|
16
16
|
const protectedPageRoutes = createRouteMatcher(
|
|
17
17
|
buildProtectedPageRoutePatterns(
|
|
18
18
|
['/dashboard', '/settings', '/profile', '/billing'],
|
|
@@ -20,25 +20,25 @@ const protectedPageRoutes = createRouteMatcher(
|
|
|
20
20
|
)
|
|
21
21
|
);
|
|
22
22
|
|
|
23
|
-
//
|
|
23
|
+
// API routes that require authentication.
|
|
24
24
|
const protectedApiRoutes = createRouteMatcher([
|
|
25
|
-
// Stripe
|
|
25
|
+
// Stripe payment APIs.
|
|
26
26
|
'/api/stripe(.*)',
|
|
27
|
-
//
|
|
27
|
+
// Credit APIs.
|
|
28
28
|
'/api/credit(.*)',
|
|
29
|
-
//
|
|
29
|
+
// Transaction APIs.
|
|
30
30
|
'/api/transaction(.*)'
|
|
31
31
|
]);
|
|
32
32
|
|
|
33
|
-
//
|
|
33
|
+
// Public API routes such as webhooks and anonymous user initialization.
|
|
34
34
|
const publicApiRoutes = createRouteMatcher([
|
|
35
35
|
// Stripe webhook
|
|
36
36
|
'/api/webhook/stripe',
|
|
37
37
|
// Clerk webhook
|
|
38
38
|
'/api/webhook/clerk/user',
|
|
39
|
-
//
|
|
39
|
+
// Anonymous user initialization.
|
|
40
40
|
'/api/user/anonymous/init',
|
|
41
|
-
//
|
|
41
|
+
// Health checks and public content APIs.
|
|
42
42
|
'/api/health',
|
|
43
43
|
'/api/legal',
|
|
44
44
|
'/api/docs',
|
|
@@ -46,8 +46,8 @@ const publicApiRoutes = createRouteMatcher([
|
|
|
46
46
|
'/api/blog'
|
|
47
47
|
]);
|
|
48
48
|
|
|
49
|
-
// v6
|
|
50
|
-
//
|
|
49
|
+
// Clerk v6 recommended usage: export clerkMiddleware(handler, options) directly.
|
|
50
|
+
// No extra wrapper function or manual request forwarding is needed.
|
|
51
51
|
export default clerkMiddleware(
|
|
52
52
|
async (auth, req: NextRequest) => {
|
|
53
53
|
const { defaultLocale, locales } = appConfig.i18n;
|
|
@@ -72,8 +72,8 @@ export default clerkMiddleware(
|
|
|
72
72
|
return authResponse;
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
-
//
|
|
76
|
-
//
|
|
75
|
+
// Handle page requests without locale prefixes according to configuration.
|
|
76
|
+
// This prevents requests from missing the [locale] route.
|
|
77
77
|
if (!hasLocalePrefix && !pathname.startsWith('/api/')) {
|
|
78
78
|
const url = req.nextUrl.clone();
|
|
79
79
|
url.pathname = `/${defaultLocale}${pathname}`;
|
|
@@ -85,7 +85,7 @@ export default clerkMiddleware(
|
|
|
85
85
|
}
|
|
86
86
|
}
|
|
87
87
|
|
|
88
|
-
//
|
|
88
|
+
// Use the default i18n middleware for all other routes.
|
|
89
89
|
|
|
90
90
|
// handle trailing slash redirect
|
|
91
91
|
if (req.nextUrl.pathname.length > 1 && req.nextUrl.pathname.endsWith("/")) {
|
|
@@ -94,12 +94,13 @@ export default clerkMiddleware(
|
|
|
94
94
|
301
|
|
95
95
|
);
|
|
96
96
|
}
|
|
97
|
-
//
|
|
97
|
+
// Default handling for other public page routes.
|
|
98
98
|
return intlMiddleware(req);
|
|
99
99
|
},
|
|
100
100
|
{ debug: appConfig.clerk.debug }
|
|
101
101
|
);
|
|
102
102
|
|
|
103
|
+
|
|
103
104
|
export const config = {
|
|
104
105
|
matcher: [
|
|
105
106
|
// Skip Next.js internals and all static files, but include API routes
|
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
|
|
3
|
-
import { CreditNavButton, CreditOverviewClient } from '@windrun-huaiin/third-ui/main/credit';
|
|
4
|
-
import type { CreditOverviewData, CreditOverviewTranslations } from '@windrun-huaiin/third-ui/main/credit';
|
|
5
|
-
import { useEffect, useState } from 'react';
|
|
6
|
-
|
|
7
|
-
interface CreditPopoverClientProps {
|
|
8
|
-
locale: string;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
interface CreditOverviewPayload {
|
|
12
|
-
data: CreditOverviewData;
|
|
13
|
-
totalLabel: string;
|
|
14
|
-
translations: CreditOverviewTranslations;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export function CreditPopoverClient({ locale }: CreditPopoverClientProps) {
|
|
18
|
-
const [payload, setPayload] = useState<CreditOverviewPayload | null>(null);
|
|
19
|
-
|
|
20
|
-
useEffect(() => {
|
|
21
|
-
const controller = new AbortController();
|
|
22
|
-
|
|
23
|
-
async function loadCreditOverview() {
|
|
24
|
-
try {
|
|
25
|
-
const response = await fetch(
|
|
26
|
-
`/api/user/credit-overview?locale=${encodeURIComponent(locale)}`,
|
|
27
|
-
{
|
|
28
|
-
credentials: 'same-origin',
|
|
29
|
-
signal: controller.signal,
|
|
30
|
-
},
|
|
31
|
-
);
|
|
32
|
-
|
|
33
|
-
if (!response.ok) {
|
|
34
|
-
return;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
const nextPayload = (await response.json()) as CreditOverviewPayload | null;
|
|
38
|
-
if (!controller.signal.aborted) {
|
|
39
|
-
setPayload(nextPayload);
|
|
40
|
-
}
|
|
41
|
-
} catch (error) {
|
|
42
|
-
if (!controller.signal.aborted) {
|
|
43
|
-
console.warn('[CreditPopover] Failed to load credit overview', error);
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
loadCreditOverview();
|
|
49
|
-
|
|
50
|
-
return () => {
|
|
51
|
-
controller.abort();
|
|
52
|
-
};
|
|
53
|
-
}, [locale]);
|
|
54
|
-
|
|
55
|
-
if (!payload) {
|
|
56
|
-
return null;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
return (
|
|
60
|
-
<CreditNavButton
|
|
61
|
-
locale={locale}
|
|
62
|
-
totalBalance={payload.data.totalBalance}
|
|
63
|
-
totalLabel={payload.totalLabel}
|
|
64
|
-
>
|
|
65
|
-
<CreditOverviewClient
|
|
66
|
-
locale={locale}
|
|
67
|
-
data={payload.data}
|
|
68
|
-
translations={payload.translations}
|
|
69
|
-
/>
|
|
70
|
-
</CreditNavButton>
|
|
71
|
-
);
|
|
72
|
-
}
|
|
@@ -1,136 +0,0 @@
|
|
|
1
|
-
import '@/server/prisma';
|
|
2
|
-
import { creditService, subscriptionService } from '@windrun-huaiin/backend-core/database';
|
|
3
|
-
import { getOptionalServerAuthUser } from '@windrun-huaiin/backend-core/auth/server';
|
|
4
|
-
import { viewLocalTime } from '@windrun-huaiin/lib/utils';
|
|
5
|
-
import { CreditNavButton } from '@windrun-huaiin/third-ui/main/credit';
|
|
6
|
-
import type { CreditOverviewData } from '@windrun-huaiin/third-ui/main/credit/server';
|
|
7
|
-
import { CreditOverview } from '@windrun-huaiin/third-ui/main/credit/server';
|
|
8
|
-
import { buildMoneyPriceData } from '@windrun-huaiin/third-ui/main/money-price/server';
|
|
9
|
-
import { moneyPriceConfig } from '@windrun-huaiin/backend-core/config/money-price';
|
|
10
|
-
import { buildInitUserContextFromEntities } from '@windrun-huaiin/backend-core/context'
|
|
11
|
-
import { getTranslations } from 'next-intl/server';
|
|
12
|
-
import { getAsNeededLocalizedUrl } from '@windrun-huaiin/lib/utils';
|
|
13
|
-
import { localePrefixAsNeeded, defaultLocale } from '@/lib/appConfig';
|
|
14
|
-
|
|
15
|
-
interface CreditPopoverProps {
|
|
16
|
-
locale: string;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export async function CreditPopover({ locale }: CreditPopoverProps) {
|
|
20
|
-
const authUser = await getOptionalServerAuthUser();
|
|
21
|
-
if (!authUser) {
|
|
22
|
-
return null;
|
|
23
|
-
}
|
|
24
|
-
const { user } = authUser;
|
|
25
|
-
|
|
26
|
-
const enableSubscriptionUpgrade = process.env.ENABLE_STRIPE_SUBSCRIPTION_UPGRADE !== 'false';
|
|
27
|
-
|
|
28
|
-
const [credit, subscription, t, moneyPriceData] = await Promise.all([
|
|
29
|
-
creditService.getCredit(user.userId),
|
|
30
|
-
subscriptionService.getActiveSubscription(user.userId),
|
|
31
|
-
getTranslations({ locale, namespace: 'credit' }),
|
|
32
|
-
buildMoneyPriceData({
|
|
33
|
-
locale,
|
|
34
|
-
currency: moneyPriceConfig.display.currency,
|
|
35
|
-
enabledBillingTypes: ['monthly', 'yearly', 'onetime'],
|
|
36
|
-
}),
|
|
37
|
-
]);
|
|
38
|
-
|
|
39
|
-
if (!credit) {
|
|
40
|
-
return null;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
const initUserContext = buildInitUserContextFromEntities({
|
|
44
|
-
user,
|
|
45
|
-
credit,
|
|
46
|
-
subscription,
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
const totalBalance =
|
|
50
|
-
(credit.balanceFree ?? 0) +
|
|
51
|
-
(credit.balancePaid ?? 0) +
|
|
52
|
-
(credit.balanceOneTimePaid ?? 0);
|
|
53
|
-
|
|
54
|
-
// 根据是否订阅,动态调整 buckets 顺序
|
|
55
|
-
// 已订阅:subscription → onetime → free
|
|
56
|
-
// 未订阅:onetime → free
|
|
57
|
-
// 为0的类型积分不展示
|
|
58
|
-
|
|
59
|
-
// 直接基于 credit 对象生成 buckets,无需额外传参
|
|
60
|
-
const buckets = [
|
|
61
|
-
...(credit.balancePaid > 0
|
|
62
|
-
? [{
|
|
63
|
-
kind: 'subscription' as const,
|
|
64
|
-
balance: credit.balancePaid,
|
|
65
|
-
limit: credit.totalPaidLimit,
|
|
66
|
-
expiresAt: viewLocalTime(credit.paidEnd)
|
|
67
|
-
}]
|
|
68
|
-
: []),
|
|
69
|
-
|
|
70
|
-
...(credit.balanceOneTimePaid > 0
|
|
71
|
-
? [{
|
|
72
|
-
kind: 'onetime' as const,
|
|
73
|
-
balance: credit.balanceOneTimePaid,
|
|
74
|
-
limit: credit.totalOneTimePaidLimit,
|
|
75
|
-
expiresAt: viewLocalTime(credit.oneTimePaidEnd)
|
|
76
|
-
}]
|
|
77
|
-
: []),
|
|
78
|
-
|
|
79
|
-
...(credit.balanceFree > 0
|
|
80
|
-
? [{
|
|
81
|
-
kind: 'free' as const,
|
|
82
|
-
balance: credit.balanceFree,
|
|
83
|
-
limit: credit.totalFreeLimit,
|
|
84
|
-
expiresAt: viewLocalTime(credit.freeEnd)
|
|
85
|
-
}]
|
|
86
|
-
: [])
|
|
87
|
-
];
|
|
88
|
-
|
|
89
|
-
// 按照项目设置来决定是否带上语言前缀
|
|
90
|
-
const pricingPageBaseUrl = getAsNeededLocalizedUrl(locale, "/pricing", localePrefixAsNeeded, defaultLocale);
|
|
91
|
-
|
|
92
|
-
const data: CreditOverviewData = {
|
|
93
|
-
totalBalance,
|
|
94
|
-
buckets,
|
|
95
|
-
pricingContext: {
|
|
96
|
-
moneyPriceData,
|
|
97
|
-
moneyPriceConfig,
|
|
98
|
-
checkoutApiEndpoint: '/api/stripe/checkout',
|
|
99
|
-
customerPortalApiEndpoint: '/api/stripe/customer-portal',
|
|
100
|
-
enableSubscriptionUpgrade,
|
|
101
|
-
initUserContext,
|
|
102
|
-
},
|
|
103
|
-
ctaBehaviors: {
|
|
104
|
-
subscribe: {
|
|
105
|
-
desktop: { kind: 'modal', mode: 'subscription' },
|
|
106
|
-
mobile: { kind: 'redirect', url: `${pricingPageBaseUrl}?initialBillingType=subscription` },
|
|
107
|
-
},
|
|
108
|
-
manage: {
|
|
109
|
-
desktop: { kind: 'auth' },
|
|
110
|
-
mobile: { kind: 'auth' },
|
|
111
|
-
},
|
|
112
|
-
onetime: {
|
|
113
|
-
desktop: { kind: 'modal', mode: 'onetime' },
|
|
114
|
-
mobile: { kind: 'redirect', url: `${pricingPageBaseUrl}?initialBillingType=onetime` },
|
|
115
|
-
},
|
|
116
|
-
},
|
|
117
|
-
};
|
|
118
|
-
|
|
119
|
-
if (subscription) {
|
|
120
|
-
data.subscription = {
|
|
121
|
-
planName: subscription.priceName ?? '',
|
|
122
|
-
periodStart: viewLocalTime(subscription.subPeriodStart),
|
|
123
|
-
periodEnd: viewLocalTime(subscription.subPeriodEnd),
|
|
124
|
-
};
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
return (
|
|
128
|
-
<CreditNavButton
|
|
129
|
-
locale={locale}
|
|
130
|
-
totalBalance={totalBalance}
|
|
131
|
-
totalLabel={t('summary.totalLabel')}
|
|
132
|
-
>
|
|
133
|
-
<CreditOverview locale={locale} data={data} />
|
|
134
|
-
</CreditNavButton>
|
|
135
|
-
);
|
|
136
|
-
}
|