create-wenuts-cli 2.1.1 → 2.3.0

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 (26) hide show
  1. package/package.json +1 -1
  2. package/template/default/eslint.config.mjs +16 -4
  3. package/template/default/package.json +11 -3
  4. package/template/default/postcss.config.mjs +12 -12
  5. package/template/default/scripts/generate-icons.mjs +27 -0
  6. package/template/default/src/api/user.ts +145 -145
  7. package/template/default/src/app/[local]/(default-layout)/layout.tsx +1 -1
  8. package/template/default/src/app/[local]/(default-layout)/page.tsx +1 -1
  9. package/template/default/src/app/[local]/(no-layout)/home/page.tsx +11 -11
  10. package/template/default/src/app/[local]/(no-layout)/login/components/LoginHandle/index.tsx +40 -44
  11. package/template/default/src/app/[local]/(no-layout)/login/page.tsx +32 -32
  12. package/template/default/src/app/[local]/layout.tsx +38 -38
  13. package/template/default/src/app/api/auth/[...nextauth]/route.ts +38 -41
  14. package/template/default/src/app/layout.tsx +17 -17
  15. package/template/default/src/components/System/ClientLayout/index.tsx +42 -42
  16. package/template/default/src/components/System/LoginModal/index.tsx +48 -53
  17. package/template/default/src/components/System/ThemeProvider/index.tsx +33 -44
  18. package/template/default/src/config/CookieMap.ts +1 -1
  19. package/template/default/src/i18n/routing.ts +8 -9
  20. package/template/default/src/libs/casdoor_provider.ts +31 -33
  21. package/template/default/src/libs/fetchCookie/serverCookies.ts +5 -5
  22. package/template/default/src/libs/nextauth.ts +38 -41
  23. package/template/default/src/middleware.ts +6 -6
  24. package/template/default/src/store/useUserStore.ts +35 -35
  25. package/template/default/src/types/payment.ts +139 -139
  26. package/template/default/src/types/user.ts +81 -87
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-wenuts-cli",
3
- "version": "2.1.1",
3
+ "version": "2.3.0",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -3,15 +3,27 @@
3
3
  * eslint-config-prettier: 关闭所有与 Prettier 冲突的 ESLint 格式化规则,
4
4
  */
5
5
 
6
+ import path from 'path';
7
+ import { fileURLToPath } from 'url';
6
8
  import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended';
7
9
  import { FlatCompat } from '@eslint/eslintrc';
10
+ import nextVitals from 'eslint-config-next/core-web-vitals';
8
11
 
9
- const compat = new FlatCompat({});
12
+ const __filename = fileURLToPath(import.meta.url);
13
+ const __dirname = path.dirname(__filename);
14
+
15
+ const compat = new FlatCompat({
16
+ baseDirectory: __dirname,
17
+ });
10
18
 
11
19
  const eslintConfig = [
12
- ...compat.config({
13
- extends: ['next/core-web-vitals', 'prettier'],
14
- }),
20
+ // ...compat.config({
21
+ // plugins: [],
22
+ // extends: ['next/core-web-vitals', 'prettier'],
23
+ // }),
24
+ // ...compat.extends('next/core-web-vitals'),
25
+ ...nextVitals,
26
+ ...compat.extends('prettier'),
15
27
  {
16
28
  rules: {
17
29
  'react-hooks/exhaustive-deps': 'off',
@@ -3,11 +3,13 @@
3
3
  "version": "0.1.0",
4
4
  "private": true,
5
5
  "scripts": {
6
- "dev": "next dev",
6
+ "dev": "concurrently \"yarn run icons:watch\" \"next dev\"",
7
7
  "build": "next build",
8
8
  "start": "next start",
9
9
  "lint": "next lint",
10
- "prepare": "husky"
10
+ "prepare": "husky",
11
+ "icons": "node scripts/generate-icons.mjs",
12
+ "icons:watch": "chokidar \"src/assets/icons/**/*.svg\" -c \"yarn run icons\""
11
13
  },
12
14
  "dependencies": {
13
15
  "@mantine/core": "^8.1.2",
@@ -26,12 +28,17 @@
26
28
  },
27
29
  "devDependencies": {
28
30
  "@eslint/eslintrc": "^3",
31
+ "@svgr/cli": "^8.1.0",
29
32
  "@tailwindcss/postcss": "^4",
30
33
  "@types/node": "^20",
31
34
  "@types/react": "^19",
32
35
  "@types/react-dom": "^19",
36
+ "chokidar-cli": "^3.0.0",
37
+ "concurrently": "^9.2.1",
33
38
  "eslint": "^9",
34
39
  "eslint-config-next": "16.1.1",
40
+ "eslint-config-prettier": "^10.1.8",
41
+ "eslint-plugin-prettier": "^5.5.4",
35
42
  "husky": "^9.0.11",
36
43
  "lint-staged": "^15.2.0",
37
44
  "postcss": "^8.5.6",
@@ -39,12 +46,13 @@
39
46
  "postcss-simple-vars": "^7.0.1",
40
47
  "prettier": "^3.7.4",
41
48
  "prettier-eslint": "^16.4.2",
49
+ "prettier-plugin-tailwindcss": "^0.7.2",
42
50
  "tailwindcss": "^4",
43
51
  "typescript": "^5"
44
52
  },
45
53
  "lint-staged": {
46
54
  "*.{js,jsx,ts,tsx}": [
47
- "eslint --max-warnings=0",
55
+ "eslint --max-warnings=0 --fix",
48
56
  "prettier --write"
49
57
  ],
50
58
  "*.{json,css,scss,md}": [
@@ -1,17 +1,17 @@
1
1
  const config = {
2
- plugins: {
3
- "@tailwindcss/postcss": {},
4
- "postcss-preset-mantine": {},
5
- "postcss-simple-vars": {
6
- variables: {
7
- "mantine-breakpoint-xs": "36em",
8
- "mantine-breakpoint-sm": "48em",
9
- "mantine-breakpoint-md": "62em",
10
- "mantine-breakpoint-lg": "75em",
11
- "mantine-breakpoint-xl": "88em",
12
- },
2
+ plugins: {
3
+ '@tailwindcss/postcss': {},
4
+ 'postcss-preset-mantine': {},
5
+ 'postcss-simple-vars': {
6
+ variables: {
7
+ 'mantine-breakpoint-xs': '36em',
8
+ 'mantine-breakpoint-sm': '48em',
9
+ 'mantine-breakpoint-md': '62em',
10
+ 'mantine-breakpoint-lg': '75em',
11
+ 'mantine-breakpoint-xl': '88em',
12
+ },
13
+ },
13
14
  },
14
- },
15
15
  };
16
16
 
17
17
  export default config;
@@ -0,0 +1,27 @@
1
+ // scripts/generate-icons.mjs
2
+ import { execSync } from 'child_process';
3
+
4
+ // 配置路径
5
+ const SVG_DIR = 'src/assets/icons'; // 存放原始 .svg 的地方
6
+ const OUT_DIR = 'src/components/icons'; // 生成组件的地方
7
+
8
+ console.log('🚀 正在将 SVG 转换为 React 组件...');
9
+
10
+ try {
11
+ // 执行 SVGR 命令
12
+ // --typescript: 生成 tsx
13
+ // --icon: 自动处理 width/height 为 1em
14
+ // --no-prettier: 禁用 prettier 插件以避免 ESM 冲突
15
+ // --out-dir: 输出目录
16
+ execSync(`npx @svgr/cli --out-dir ${OUT_DIR} --typescript --icon --no-prettier ${SVG_DIR}`, {
17
+ stdio: 'inherit',
18
+ });
19
+ // 使用项目的 Prettier 配置重新格式化生成的文件
20
+ execSync(`npx prettier --write "${OUT_DIR}/**/*.{ts,tsx}"`, {
21
+ stdio: 'inherit',
22
+ });
23
+
24
+ console.log('✅ 转换完成!');
25
+ } catch (error) {
26
+ console.error('❌ 转换失败:', error.message);
27
+ }
@@ -1,149 +1,149 @@
1
- import fetch from "@/libs/fetch";
1
+ import fetch from '@/libs/fetch';
2
2
  import {
3
- IApiKey,
4
- CreateInvoiceResult,
5
- FeedBackReqParams,
6
- IManageSubscriptionResult,
7
- INoticeResult,
8
- IUpdateUserNameParams,
9
- IUserDetailResp,
10
- IUserLoginArgs,
11
- IUserLoginState,
12
- IUserProfile,
13
- IChargeParams,
14
- IChargeResult,
15
- } from "@/types/user";
3
+ IApiKey,
4
+ CreateInvoiceResult,
5
+ FeedBackReqParams,
6
+ IManageSubscriptionResult,
7
+ INoticeResult,
8
+ IUpdateUserNameParams,
9
+ IUserDetailResp,
10
+ IUserLoginArgs,
11
+ IUserLoginState,
12
+ IUserProfile,
13
+ IChargeParams,
14
+ IChargeResult,
15
+ } from '@/types/user';
16
16
 
17
17
  export default class UserApi {
18
- /**
19
- * 登录接口
20
- * @param args.srcType 登录类型 'casdoor' | 'discord' | 'facebook' | 'google' | 'googleOneTap' | 'twitter';
21
- * @param args.code 测试环境和本地环境专用 casdoor 登录专用
22
- * @param args.token google 登录专用, google 回调的 token.access_token
23
- * @param args.credential googleOneTap 登录专用, google one tap 回调的 credential
24
- */
25
- static userLogin(args: IUserLoginArgs) {
26
- return fetch.post<IUserLoginState>("/user/auth", args);
27
- }
28
-
29
- /**
30
- * 获取用户信息接口
31
- */
32
- static userProfile() {
33
- return fetch.get<IUserProfile>("/user/profile");
34
- }
35
-
36
- /**
37
- * 更新用户信息
38
- * @param args.avatar 头像地址,需要拼接好 domain 的全地址
39
- * @param args.name 用户名
40
- */
41
- static userUpdate(args: { avatar?: string; name?: string }) {
42
- return fetch.post<{ avatar: string }>("/user/update", args);
43
- }
44
-
45
- /**
46
- * 注销账户
47
- */
48
- static userDelete() {
49
- return fetch.post("/user/delete");
50
- }
51
-
52
- /**
53
- * 自动分享到社区
54
- * @returns
55
- */
56
- static updateShareCommunity() {
57
- return fetch.post("/user/update-auto-send");
58
- }
59
-
60
- /**
61
- * 关注用户
62
- * @param args followerId
63
- * @returns 结果
64
- */
65
- static followUser(args: { followerId: string }) {
66
- return fetch.post("/user/favourite", args);
67
- }
68
-
69
- /**
70
- * 取消关注用户
71
- * @param args followerId
72
- * @returns 结果
73
- */
74
- static unFollowUser(args: { followerId: string }) {
75
- return fetch.post("/user/unfavourite", args);
76
- }
77
-
78
- static getUserInfo(params: { userId: string }) {
79
- return fetch.get<IUserDetailResp>("/user/info", params);
80
- }
81
-
82
- static updateNsfw() {
83
- return fetch.post<{
84
- displayNSFW: boolean;
85
- }>("/user/update-nsfw");
86
- }
87
-
88
- static noticeList(args: { page: string }) {
89
- return fetch.get<INoticeResult>("/notice/list", args);
90
- }
91
-
92
- static updateAvatar(imgPath: string) {
93
- return fetch.post("/user/update-avatar", { imgPath });
94
- }
95
-
96
- static updateUsername(params: IUpdateUserNameParams) {
97
- return fetch.post("/user/update-username", params);
98
- }
99
-
100
- static deleteUser() {
101
- return fetch.post("/user/delete");
102
- }
103
-
104
- static manageSubscription() {
105
- return fetch.get<IManageSubscriptionResult>("/user/portal");
106
- }
107
-
108
- static cancelPaypalSub() {
109
- return fetch.post("/payment/cancel-sub");
110
- }
111
-
112
- static createFeedBack(args: FeedBackReqParams) {
113
- return fetch.post("/feedback/create", args);
114
- }
115
-
116
- /**
117
- * 创建API密钥
118
- * @returns
119
- */
120
- static createKey() {
121
- return fetch.post<IApiKey>("/user/create-key");
122
- }
123
-
124
- /**
125
- * 删除API密钥
126
- * @param args.id
127
- * @returns
128
- */
129
- static deleteKey(args: { id: string }) {
130
- return fetch.post("/user/delete-key", args);
131
- }
132
-
133
- /**
134
- * 获取API密钥列表
135
- * @returns
136
- */
137
- static listKey() {
138
- return fetch.get<{ keys: IApiKey[] }>("/user/list-key");
139
- }
140
-
141
- // 创建支付凭证
142
- static createInvoice() {
143
- return fetch.post<CreateInvoiceResult>("/payment/create-invoice");
144
- }
145
-
146
- static checkIn(args: IChargeParams) {
147
- return fetch.post<IChargeResult>("/user/charge", args);
148
- }
18
+ /**
19
+ * 登录接口
20
+ * @param args.srcType 登录类型 'casdoor' | 'discord' | 'facebook' | 'google' | 'googleOneTap' | 'twitter';
21
+ * @param args.code 测试环境和本地环境专用 casdoor 登录专用
22
+ * @param args.token google 登录专用, google 回调的 token.access_token
23
+ * @param args.credential googleOneTap 登录专用, google one tap 回调的 credential
24
+ */
25
+ static userLogin(args: IUserLoginArgs) {
26
+ return fetch.post<IUserLoginState>('/user/auth', args);
27
+ }
28
+
29
+ /**
30
+ * 获取用户信息接口
31
+ */
32
+ static userProfile() {
33
+ return fetch.get<IUserProfile>('/user/profile');
34
+ }
35
+
36
+ /**
37
+ * 更新用户信息
38
+ * @param args.avatar 头像地址,需要拼接好 domain 的全地址
39
+ * @param args.name 用户名
40
+ */
41
+ static userUpdate(args: { avatar?: string; name?: string }) {
42
+ return fetch.post<{ avatar: string }>('/user/update', args);
43
+ }
44
+
45
+ /**
46
+ * 注销账户
47
+ */
48
+ static userDelete() {
49
+ return fetch.post('/user/delete');
50
+ }
51
+
52
+ /**
53
+ * 自动分享到社区
54
+ * @returns
55
+ */
56
+ static updateShareCommunity() {
57
+ return fetch.post('/user/update-auto-send');
58
+ }
59
+
60
+ /**
61
+ * 关注用户
62
+ * @param args followerId
63
+ * @returns 结果
64
+ */
65
+ static followUser(args: { followerId: string }) {
66
+ return fetch.post('/user/favourite', args);
67
+ }
68
+
69
+ /**
70
+ * 取消关注用户
71
+ * @param args followerId
72
+ * @returns 结果
73
+ */
74
+ static unFollowUser(args: { followerId: string }) {
75
+ return fetch.post('/user/unfavourite', args);
76
+ }
77
+
78
+ static getUserInfo(params: { userId: string }) {
79
+ return fetch.get<IUserDetailResp>('/user/info', params);
80
+ }
81
+
82
+ static updateNsfw() {
83
+ return fetch.post<{
84
+ displayNSFW: boolean;
85
+ }>('/user/update-nsfw');
86
+ }
87
+
88
+ static noticeList(args: { page: string }) {
89
+ return fetch.get<INoticeResult>('/notice/list', args);
90
+ }
91
+
92
+ static updateAvatar(imgPath: string) {
93
+ return fetch.post('/user/update-avatar', { imgPath });
94
+ }
95
+
96
+ static updateUsername(params: IUpdateUserNameParams) {
97
+ return fetch.post('/user/update-username', params);
98
+ }
99
+
100
+ static deleteUser() {
101
+ return fetch.post('/user/delete');
102
+ }
103
+
104
+ static manageSubscription() {
105
+ return fetch.get<IManageSubscriptionResult>('/user/portal');
106
+ }
107
+
108
+ static cancelPaypalSub() {
109
+ return fetch.post('/payment/cancel-sub');
110
+ }
111
+
112
+ static createFeedBack(args: FeedBackReqParams) {
113
+ return fetch.post('/feedback/create', args);
114
+ }
115
+
116
+ /**
117
+ * 创建API密钥
118
+ * @returns
119
+ */
120
+ static createKey() {
121
+ return fetch.post<IApiKey>('/user/create-key');
122
+ }
123
+
124
+ /**
125
+ * 删除API密钥
126
+ * @param args.id
127
+ * @returns
128
+ */
129
+ static deleteKey(args: { id: string }) {
130
+ return fetch.post('/user/delete-key', args);
131
+ }
132
+
133
+ /**
134
+ * 获取API密钥列表
135
+ * @returns
136
+ */
137
+ static listKey() {
138
+ return fetch.get<{ keys: IApiKey[] }>('/user/list-key');
139
+ }
140
+
141
+ // 创建支付凭证
142
+ static createInvoice() {
143
+ return fetch.post<CreateInvoiceResult>('/payment/create-invoice');
144
+ }
145
+
146
+ static checkIn(args: IChargeParams) {
147
+ return fetch.post<IChargeResult>('/user/charge', args);
148
+ }
149
149
  }
@@ -1,5 +1,5 @@
1
1
  const DefaultLayout = ({ children }: { children: React.ReactNode }) => {
2
- return <div>{children}</div>;
2
+ return <div>{children}</div>;
3
3
  };
4
4
 
5
5
  export default DefaultLayout;
@@ -1,5 +1,5 @@
1
1
  const Page = () => {
2
- return <div>Page</div>;
2
+ return <div>Page</div>;
3
3
  };
4
4
 
5
5
  export default Page;
@@ -1,16 +1,16 @@
1
- "use client";
2
- import useUserStore from "@/store/useUserStore";
3
- import { Button } from "@mantine/core";
4
- import { useTranslations } from "next-intl";
1
+ 'use client';
2
+ import useUserStore from '@/store/useUserStore';
3
+ import { Button } from '@mantine/core';
4
+ import { useTranslations } from 'next-intl';
5
5
 
6
6
  const Page = () => {
7
- const t = useTranslations("HomePage");
8
- const openLoginModal = useUserStore((state) => state.openLoginModal);
9
- return (
10
- <div>
11
- <Button onClick={openLoginModal}>{t("title")}</Button>
12
- </div>
13
- );
7
+ const t = useTranslations('HomePage');
8
+ const openLoginModal = useUserStore(state => state.openLoginModal);
9
+ return (
10
+ <div>
11
+ <Button onClick={openLoginModal}>{t('title')}</Button>
12
+ </div>
13
+ );
14
14
  };
15
15
 
16
16
  export default Page;
@@ -1,55 +1,51 @@
1
- "use client";
2
- import React, { useEffect } from "react";
3
- import { signIn } from "next-auth/react";
1
+ 'use client';
2
+ import React, { useEffect } from 'react';
3
+ import { signIn } from 'next-auth/react';
4
4
 
5
- import { Loader } from "@mantine/core";
5
+ import { Loader } from '@mantine/core';
6
6
 
7
7
  interface IProps {
8
- isLogin: boolean;
8
+ isLogin: boolean;
9
9
  }
10
10
 
11
11
  const LoginHandle: React.FC<IProps> = ({ isLogin }) => {
12
- useEffect(() => {
13
- if (isLogin) {
14
- if (window.opener) {
15
- window.opener.postMessage(
16
- "login_success",
17
- process.env.NEXT_PUBLIC_DOMAIN
18
- );
19
- window.close(); // 子窗口自己关闭,不受 COOP 限制
20
- } else {
21
- // 防止用户直接在浏览器打开登录页面
22
- window.location.href = process.env.NEXT_PUBLIC_DOMAIN + "/";
23
- }
24
- } else {
25
- loginHandler();
26
- }
27
- }, []);
12
+ /**
13
+ * 登录事件代理
14
+ */
15
+ const loginHandler = () => {
16
+ const isPro = process.env.NEXT_PUBLIC_NODE_ENV !== 'dev';
28
17
 
29
- /**
30
- * 登录事件代理
31
- */
32
- const loginHandler = () => {
33
- const isPro = process.env.NEXT_PUBLIC_NODE_ENV !== "dev";
18
+ if (isPro) {
19
+ signIn('google', {
20
+ callbackUrl: process.env.NEXT_PUBLIC_DOMAIN + '/login/',
21
+ redirect: true,
22
+ });
23
+ } else {
24
+ signIn('casdoor', {
25
+ callbackUrl: process.env.NEXT_PUBLIC_DOMAIN + '/login/',
26
+ redirect: true,
27
+ });
28
+ }
29
+ };
30
+ useEffect(() => {
31
+ if (isLogin) {
32
+ if (window.opener) {
33
+ window.opener.postMessage('login_success', process.env.NEXT_PUBLIC_DOMAIN);
34
+ window.close(); // 子窗口自己关闭,不受 COOP 限制
35
+ } else {
36
+ // 防止用户直接在浏览器打开登录页面
37
+ window.location.href = process.env.NEXT_PUBLIC_DOMAIN + '/';
38
+ }
39
+ } else {
40
+ loginHandler();
41
+ }
42
+ }, []);
34
43
 
35
- if (isPro) {
36
- signIn("google", {
37
- callbackUrl: process.env.NEXT_PUBLIC_DOMAIN + "/login/",
38
- redirect: true,
39
- });
40
- } else {
41
- signIn("casdoor", {
42
- callbackUrl: process.env.NEXT_PUBLIC_DOMAIN + "/login/",
43
- redirect: true,
44
- });
45
- }
46
- };
47
-
48
- return (
49
- <div className="w-screen h-screen flex justify-center items-center">
50
- <Loader type="dots" />
51
- </div>
52
- );
44
+ return (
45
+ <div className="flex h-screen w-screen items-center justify-center">
46
+ <Loader type="dots" />
47
+ </div>
48
+ );
53
49
  };
54
50
 
55
51
  export default LoginHandle;
@@ -1,41 +1,41 @@
1
- import React from "react";
2
- import LoginHandle from "./components/LoginHandle";
3
- import { ScrollArea } from "@mantine/core";
4
- import { cookies } from "next/headers";
5
- import CookieMap from "@/config/CookieMap";
1
+ import React from 'react';
2
+ import LoginHandle from './components/LoginHandle';
3
+ import { ScrollArea } from '@mantine/core';
4
+ import { cookies } from 'next/headers';
5
+ import CookieMap from '@/config/CookieMap';
6
6
 
7
- import style from "./style.module.css";
7
+ import style from './style.module.css';
8
8
 
9
9
  interface IProps {
10
- searchParams: { redirect: string };
10
+ searchParams: { redirect: string };
11
11
  }
12
12
 
13
13
  const LoginPage: React.FC<IProps> = async ({ searchParams }) => {
14
- const cookieStore = await cookies();
15
-
16
- const userState = cookieStore.get(CookieMap.UserState);
17
-
18
- let isLogin = false;
19
-
20
- if (userState) {
21
- isLogin = true;
22
- }
23
-
24
- return (
25
- <ScrollArea
26
- className="h-screen w-screen"
27
- type="never"
28
- classNames={{
29
- viewport: `${style.custom_scroll}`,
30
- }}
31
- >
32
- <div className="w-full h-full p-3 md:p-6 bg-white">
33
- <div className="flex md:h-full md:grid gap-6 md:gap-12 items-center flex-col-reverse">
34
- <LoginHandle isLogin={isLogin} />
35
- </div>
36
- </div>
37
- </ScrollArea>
38
- );
14
+ const cookieStore = await cookies();
15
+
16
+ const userState = cookieStore.get(CookieMap.UserState);
17
+
18
+ let isLogin = false;
19
+
20
+ if (userState) {
21
+ isLogin = true;
22
+ }
23
+
24
+ return (
25
+ <ScrollArea
26
+ className="h-screen w-screen"
27
+ type="never"
28
+ classNames={{
29
+ viewport: `${style.custom_scroll}`,
30
+ }}
31
+ >
32
+ <div className="h-full w-full bg-white p-3 md:p-6">
33
+ <div className="flex flex-col-reverse items-center gap-6 md:grid md:h-full md:gap-12">
34
+ <LoginHandle isLogin={isLogin} />
35
+ </div>
36
+ </div>
37
+ </ScrollArea>
38
+ );
39
39
  };
40
40
 
41
41
  export default LoginPage;