create-wenuts-cli 1.0.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 (51) hide show
  1. package/dist/index.d.ts +2 -0
  2. package/dist/index.js +168 -0
  3. package/package.json +35 -0
  4. package/template/default/.env.development +19 -0
  5. package/template/default/README.md +36 -0
  6. package/template/default/envConfig/.env.development +26 -0
  7. package/template/default/envConfig/.env.production +23 -0
  8. package/template/default/eslint.config.mjs +15 -0
  9. package/template/default/next.config.ts +10 -0
  10. package/template/default/package.json +40 -0
  11. package/template/default/postcss.config.mjs +17 -0
  12. package/template/default/public/file.svg +1 -0
  13. package/template/default/public/globe.svg +1 -0
  14. package/template/default/public/next.svg +1 -0
  15. package/template/default/public/vercel.svg +1 -0
  16. package/template/default/public/window.svg +1 -0
  17. package/template/default/src/api/user.ts +149 -0
  18. package/template/default/src/app/[local]/(default-layout)/layout.tsx +5 -0
  19. package/template/default/src/app/[local]/(default-layout)/page.tsx +5 -0
  20. package/template/default/src/app/[local]/(no-layout)/home/page.tsx +16 -0
  21. package/template/default/src/app/[local]/(no-layout)/login/components/LoginHandle/index.tsx +55 -0
  22. package/template/default/src/app/[local]/(no-layout)/login/page.tsx +41 -0
  23. package/template/default/src/app/[local]/(no-layout)/login/style.module.css +4 -0
  24. package/template/default/src/app/[local]/layout.tsx +54 -0
  25. package/template/default/src/app/api/auth/[...nextauth]/route.ts +54 -0
  26. package/template/default/src/app/favicon.ico +0 -0
  27. package/template/default/src/app/globals.css +27 -0
  28. package/template/default/src/app/layout.tsx +26 -0
  29. package/template/default/src/appConfig.ts +37 -0
  30. package/template/default/src/components/System/ClientLayout/index.tsx +69 -0
  31. package/template/default/src/components/System/LoginModal/index.tsx +61 -0
  32. package/template/default/src/components/System/ThemeProvider/index.tsx +55 -0
  33. package/template/default/src/config/CookieMap.ts +3 -0
  34. package/template/default/src/config/LocalStorageMap.ts +11 -0
  35. package/template/default/src/i18n/request.ts +17 -0
  36. package/template/default/src/i18n/routing.ts +15 -0
  37. package/template/default/src/lang/en.json +6 -0
  38. package/template/default/src/libs/casdoor_provider.ts +39 -0
  39. package/template/default/src/libs/dataAnalytics.ts +176 -0
  40. package/template/default/src/libs/fetch.ts +176 -0
  41. package/template/default/src/libs/fetchCookie/clientCookies.ts +5 -0
  42. package/template/default/src/libs/fetchCookie/getCookie.ts +15 -0
  43. package/template/default/src/libs/fetchCookie/serverCookies.ts +10 -0
  44. package/template/default/src/libs/localCache.ts +28 -0
  45. package/template/default/src/libs/nextauth.ts +49 -0
  46. package/template/default/src/libs/seo.ts +89 -0
  47. package/template/default/src/middleware.ts +11 -0
  48. package/template/default/src/store/useUserStore.ts +42 -0
  49. package/template/default/src/types/payment.ts +163 -0
  50. package/template/default/src/types/user.ts +137 -0
  51. package/template/default/tsconfig.json +27 -0
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,168 @@
1
+ #!/usr/bin/env node
2
+ import inquirer from "inquirer";
3
+ import chalk from "chalk";
4
+ import fs from "fs-extra";
5
+ import path from "path";
6
+ import { fileURLToPath } from "url";
7
+ import { execSync } from "child_process";
8
+ import ora from "ora";
9
+ // Node.js 在 ES 模块中没有 __filename 和 __dirname 变量,需要手动获取
10
+ const __filename = fileURLToPath(import.meta.url);
11
+ const __dirname = path.dirname(__filename);
12
+ function checkRemoteRepoExists(remoteRepoUrl, targetDir) {
13
+ const spinner = ora("正在检查远程仓库是否存在...").start();
14
+ let isRemoteRepoExists = false;
15
+ try {
16
+ execSync(`git ls-remote ${remoteRepoUrl}`, { stdio: "ignore" });
17
+ isRemoteRepoExists = true;
18
+ }
19
+ catch (error) {
20
+ isRemoteRepoExists = false;
21
+ }
22
+ if (isRemoteRepoExists) {
23
+ spinner.succeed("远程仓库存在");
24
+ // 如果远程仓库存在,则尝试关联
25
+ linkRemoteRepo(remoteRepoUrl, targetDir);
26
+ }
27
+ else {
28
+ spinner.fail("远程仓库不存在, 请检查远程仓库地址是否正确。");
29
+ }
30
+ return isRemoteRepoExists;
31
+ }
32
+ function installDependencies(packageManager, targetDir) {
33
+ const spinner = ora("正在安装依赖...").start();
34
+ let isInstallDependenciesSuccess = false;
35
+ try {
36
+ console.log(chalk.gray(`执行命令: ${packageManager} install`));
37
+ console.log(chalk.gray(`目标目录: ${targetDir}`));
38
+ execSync(`${packageManager} install`, {
39
+ cwd: targetDir,
40
+ stdio: "inherit", // 显示安装过程的输出
41
+ });
42
+ isInstallDependenciesSuccess = true;
43
+ }
44
+ catch (error) {
45
+ isInstallDependenciesSuccess = false;
46
+ }
47
+ if (isInstallDependenciesSuccess) {
48
+ spinner.succeed("依赖安装成功");
49
+ }
50
+ else {
51
+ spinner.fail("依赖安装失败, 请手动安装!!");
52
+ }
53
+ return isInstallDependenciesSuccess;
54
+ }
55
+ function linkRemoteRepo(remoteRepoUrl, targetDir) {
56
+ const spinner = ora("正在关联远程仓库...").start();
57
+ let isLinkRemoteRepoSuccess = false;
58
+ try {
59
+ execSync(`git remote add origin ${remoteRepoUrl}`, { cwd: targetDir });
60
+ isLinkRemoteRepoSuccess = true;
61
+ }
62
+ catch (error) {
63
+ isLinkRemoteRepoSuccess = false;
64
+ }
65
+ if (isLinkRemoteRepoSuccess) {
66
+ spinner.succeed("远程仓库关联成功");
67
+ }
68
+ else {
69
+ spinner.fail("远程仓库关联失败, 请手动关联!!");
70
+ }
71
+ return isLinkRemoteRepoSuccess;
72
+ }
73
+ const endSuccessConsole = (projectName, packageManager, dependenciesIsSuccess) => {
74
+ console.log(chalk.green(`\n✅ 项目 ${projectName} 拉取成功!`));
75
+ console.log();
76
+ console.log(chalk.yellow(` cd ${projectName}`));
77
+ if (!dependenciesIsSuccess) {
78
+ console.log(chalk.yellow(` ${packageManager} install`));
79
+ }
80
+ console.log(chalk.yellow(` ${packageManager} run dev`));
81
+ };
82
+ async function run() {
83
+ console.log(chalk.cyan("\n欢迎使用 create-wenuts-template 脚手架!"));
84
+ const { projectName } = await inquirer.prompt([
85
+ {
86
+ type: "input",
87
+ name: "projectName",
88
+ message: "请输入项目名称:",
89
+ validate: (input) => !!input || "项目名称不能为空",
90
+ },
91
+ ]);
92
+ const { packageManager } = await inquirer.prompt([
93
+ {
94
+ type: "list",
95
+ name: "packageManager",
96
+ message: "请选择包管理器:",
97
+ choices: [
98
+ {
99
+ name: "npm",
100
+ value: "npm",
101
+ },
102
+ {
103
+ name: "yarn",
104
+ value: "yarn",
105
+ },
106
+ {
107
+ name: "pnpm",
108
+ value: "pnpm",
109
+ },
110
+ ],
111
+ },
112
+ ]);
113
+ const { templateName } = await inquirer.prompt([
114
+ {
115
+ type: "list",
116
+ name: "templateName",
117
+ message: "请选择模版:",
118
+ choices: [
119
+ {
120
+ name: "默认模版(Nextjs 15, TailwindCSS 4, TypeScript, next-auth, next-intl, mantine)",
121
+ value: "default",
122
+ },
123
+ ],
124
+ },
125
+ ]);
126
+ const targetDir = path.resolve(process.cwd(), projectName);
127
+ if (fs.existsSync(targetDir)) {
128
+ console.log(chalk.red("❌ 该目录已存在,请更换项目名称或先删除原目录。"));
129
+ process.exit(1);
130
+ }
131
+ try {
132
+ // 这里预留复制 template 目录的逻辑
133
+ const templateDir = path.resolve(__dirname, `../template/${templateName}`);
134
+ await fs.copy(templateDir, targetDir);
135
+ console.log(chalk.green(`\n✅ 项目 ${projectName} 成功拉取${templateName}模版代码!`));
136
+ }
137
+ catch (error) {
138
+ console.log(chalk.red("❌ 模版代码拉取失败,请重新执行命令"));
139
+ process.exit(1);
140
+ }
141
+ // 初始化git
142
+ execSync("git init", { cwd: targetDir });
143
+ console.log(chalk.green(`\n✅ git 初始化成功!\n`));
144
+ // 是否需要关联远程仓库
145
+ const { isNeedLinkRemoteRepo } = await inquirer.prompt([
146
+ {
147
+ type: "confirm",
148
+ name: "isNeedLinkRemoteRepo",
149
+ message: "是否需要关联远程仓库?",
150
+ default: true,
151
+ },
152
+ ]);
153
+ if (isNeedLinkRemoteRepo) {
154
+ const { remoteRepoUrl } = await inquirer.prompt([
155
+ {
156
+ type: "input",
157
+ name: "remoteRepoUrl",
158
+ message: "请输入远程仓库地址:",
159
+ },
160
+ ]);
161
+ checkRemoteRepoExists(remoteRepoUrl, targetDir);
162
+ }
163
+ const isInstallDependenciesSuccess = installDependencies(packageManager, targetDir);
164
+ endSuccessConsole(projectName, packageManager, isInstallDependenciesSuccess);
165
+ // 成功退出
166
+ process.exit(0);
167
+ }
168
+ run();
package/package.json ADDED
@@ -0,0 +1,35 @@
1
+ {
2
+ "name": "create-wenuts-cli",
3
+ "version": "1.0.0",
4
+ "description": "",
5
+ "main": "dist/index.js",
6
+ "bin": {
7
+ "create-wenuts-cli": "dist/index.js"
8
+ },
9
+ "type": "module",
10
+ "scripts": {
11
+ "dev": "tsx src/index.ts",
12
+ "build": "tsc",
13
+ "watch": "tsc --watch"
14
+ },
15
+ "files": [
16
+ "dist",
17
+ "template"
18
+ ],
19
+ "keywords": [],
20
+ "author": "",
21
+ "license": "ISC",
22
+ "dependencies": {
23
+ "@types/fs-extra": "^11.0.4",
24
+ "@types/inquirer": "^9.0.8",
25
+ "chalk": "^5.0.0",
26
+ "fs-extra": "^11.0.0",
27
+ "inquirer": "^9.0.0",
28
+ "ora": "^8.2.0"
29
+ },
30
+ "devDependencies": {
31
+ "@types/node": "^20.0.0",
32
+ "tsx": "^4.0.0",
33
+ "typescript": "^5.0.0"
34
+ }
35
+ }
@@ -0,0 +1,19 @@
1
+ NEXT_PUBLIC_NODE_ENV="dev"
2
+ NEXTAUTH_URL="***"
3
+ NEXT_PUBLIC_DOMAIN="***"
4
+ NEXT_PUBLIC_BASE_URL="***"
5
+ # NEXT_PUBLIC_BASE_URL="***"
6
+ NEXT_PUBLIC_APP_DOMAIN="***"
7
+ NEXTAUTH_SECRET="***"
8
+ NEXT_PUBLIC_GOOGLE_CLIENT_ID="***"
9
+ GOOGLE_SECRET="***"
10
+
11
+
12
+ NEXT_PUBLIC_CASDOOR_CLIENT_ID="***"
13
+ NEXT_PUBLIC_CASDOOR_ISSUER="***"
14
+
15
+ NEXT_PUBLIC_ALi_CLOUD_DOMAIN='***'
16
+ NEXT_PUBLIC_ALi_CLOUD_BUCKET='***'
17
+ NEXT_PUBLIC_ALi_CLOUD_REGION='***'
18
+ NEXT_PUBLIC_ALi_CLOUD_ACCESS_KEY_ID="***"
19
+ NEXT_PUBLIC_ALi_CLOUD_ACCESS_KEY_SECRET="***"
@@ -0,0 +1,36 @@
1
+ This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app).
2
+
3
+ ## Getting Started
4
+
5
+ First, run the development server:
6
+
7
+ ```bash
8
+ npm run dev
9
+ # or
10
+ yarn dev
11
+ # or
12
+ pnpm dev
13
+ # or
14
+ bun dev
15
+ ```
16
+
17
+ Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
18
+
19
+ You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
20
+
21
+ This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.
22
+
23
+ ## Learn More
24
+
25
+ To learn more about Next.js, take a look at the following resources:
26
+
27
+ - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
28
+ - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
29
+
30
+ You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!
31
+
32
+ ## Deploy on Vercel
33
+
34
+ The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
35
+
36
+ Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.
@@ -0,0 +1,26 @@
1
+ # 全部替换为****
2
+ NEXT_PUBLIC_NODE_ENV="dev"
3
+ NEXTAUTH_URL="***"
4
+ NEXT_PUBLIC_DOMAIN="***"
5
+ NEXT_PUBLIC_BASE_URL="***"
6
+ NEXT_PUBLIC_APP_DOMAIN="***"
7
+ NEXTAUTH_SECRET="***"
8
+ NEXT_PUBLIC_GOOGLE_CLIENT_ID="***"
9
+ GOOGLE_SECRET="***"
10
+ NEXT_PUBLIC_GOOGLE_LOGIN_URL="***"
11
+ NEXT_PUBLIC_OSS_DOMAIN ="***"
12
+ NEXT_PUBLIC_DISCORD_CLIENT_ID="***" # discord id
13
+ NEXT_PUBLIC_DISCORD_SECRET="***" # discord secret
14
+ NEXT_PUBLIC_DISCORD_LOGIN_URL="***" # discord登录
15
+ NEXT_PUBLIC_TWITTER_CLIENT_ID="***" # twitter client id
16
+ NEXT_PUBLIC_TWITTER_SECRET="***" # twitter client secret
17
+ NEXT_PUBLIC_TWITTER_LOGIN_URL="***" # twitter登录
18
+ NEXT_PUBLIC_FACEBOOK_CLIENT_ID="***" # facebook client id
19
+ NEXT_PUBLIC_FACEBOOK_SECRET="***" # facebook client secret
20
+ NEXT_PUBLIC_FACEBOOK_LOGIN_URL="***" # facebook登录
21
+
22
+ NEXT_PUBLIC_ALi_CLOUD_DOMAIN='***'
23
+ NEXT_PUBLIC_ALi_CLOUD_BUCKET='***'
24
+ NEXT_PUBLIC_ALi_CLOUD_REGION='***'
25
+ NEXT_PUBLIC_ALi_CLOUD_ACCESS_KEY_ID="***"
26
+ NEXT_PUBLIC_ALi_CLOUD_ACCESS_KEY_SECRET="***"
@@ -0,0 +1,23 @@
1
+ NEXT_PUBLIC_NODE_ENV="pro"
2
+ NEXTAUTH_URL="***"
3
+ NEXT_PUBLIC_DOMAIN="***"
4
+ NEXT_PUBLIC_BASE_URL="***"
5
+ NEXT_PUBLIC_APP_DOMAIN="***"
6
+ NEXTAUTH_SECRET="***"
7
+ NEXT_PUBLIC_GOOGLE_CLIENT_ID="***"
8
+ GOOGLE_SECRET="***"
9
+ NEXT_PUBLIC_GOOGLE_LOGIN_URL="***"
10
+ NEXT_PUBLIC_OSS_DOMAIN ="***"
11
+ NEXT_PUBLIC_DISCORD_CLIENT_ID="***" # discord id
12
+ NEXT_PUBLIC_DISCORD_SECRET="***" # discord secret
13
+ NEXT_PUBLIC_DISCORD_LOGIN_URL="***" # discord登录
14
+ NEXT_PUBLIC_TWITTER_CLIENT_ID="***" # twitter client id
15
+ NEXT_PUBLIC_TWITTER_SECRET="***" # twitter client secret
16
+ NEXT_PUBLIC_TWITTER_LOGIN_URL="***" # twitter登录
17
+ NEXT_PUBLIC_FACEBOOK_CLIENT_ID="***" # facebook client id
18
+ NEXT_PUBLIC_FACEBOOK_SECRET="***" # facebook client secret
19
+ NEXT_PUBLIC_FACEBOOK_LOGIN_URL="***" # facebook登录
20
+ NEXT_PUBLIC_ALi_CLOUD_BUCKET='***' # 阿里云bucket
21
+ NEXT_PUBLIC_ALi_CLOUD_REGION='***' # 阿里云region
22
+ NEXT_PUBLIC_ALi_CLOUD_ACCESS_KEY_ID="***" # 阿里云access key id
23
+ NEXT_PUBLIC_ALi_CLOUD_ACCESS_KEY_SECRET="***" # 阿里云access key secret
@@ -0,0 +1,15 @@
1
+ export default {
2
+ extends: ["next/core-web-vitals", "plugin:prettier/recommended"],
3
+ env: {
4
+ browser: true,
5
+ node: true,
6
+ es6: true,
7
+ },
8
+ rules: {
9
+ "prettier/prettier": 2,
10
+ "import/no-anonymous-default-export": 0,
11
+ "react-hooks/exhaustive-deps": 0,
12
+ "react/no-children-prop": 0,
13
+ "react/no-unescaped-entities": 0,
14
+ },
15
+ };
@@ -0,0 +1,10 @@
1
+ import type { NextConfig } from "next";
2
+ import createNextIntlPlugin from "next-intl/plugin";
3
+
4
+ const withNextIntl = createNextIntlPlugin();
5
+
6
+ const nextConfig: NextConfig = {
7
+ /* config options here */
8
+ };
9
+
10
+ export default withNextIntl(nextConfig);
@@ -0,0 +1,40 @@
1
+ {
2
+ "name": "wenuts-template-default",
3
+ "version": "0.1.0",
4
+ "private": true,
5
+ "scripts": {
6
+ "dev": "next dev",
7
+ "build": "next build",
8
+ "start": "next start",
9
+ "lint": "next lint"
10
+ },
11
+ "dependencies": {
12
+ "@mantine/core": "^8.1.2",
13
+ "@mantine/hooks": "^8.1.2",
14
+ "@types/js-cookie": "^3.0.6",
15
+ "js-cookie": "^3.0.5",
16
+ "next": "15.3.5",
17
+ "next-auth": "^4.24.11",
18
+ "next-extra": "^0.6.2",
19
+ "next-intl": "^4.3.4",
20
+ "query-string": "^9.2.2",
21
+ "react": "^19.0.0",
22
+ "react-dom": "^19.0.0",
23
+ "sonner": "^2.0.6",
24
+ "zustand": "^5.0.6"
25
+ },
26
+ "devDependencies": {
27
+ "@eslint/eslintrc": "^3",
28
+ "@tailwindcss/postcss": "^4",
29
+ "@types/node": "^20",
30
+ "@types/react": "^19",
31
+ "@types/react-dom": "^19",
32
+ "eslint": "^9",
33
+ "eslint-config-next": "15.3.5",
34
+ "postcss": "^8.5.6",
35
+ "postcss-preset-mantine": "^1.18.0",
36
+ "postcss-simple-vars": "^7.0.1",
37
+ "tailwindcss": "^4",
38
+ "typescript": "^5"
39
+ }
40
+ }
@@ -0,0 +1,17 @@
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
+ },
13
+ },
14
+ },
15
+ };
16
+
17
+ export default config;
@@ -0,0 +1 @@
1
+ <svg fill="none" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M14.5 13.5V5.41a1 1 0 0 0-.3-.7L9.8.29A1 1 0 0 0 9.08 0H1.5v13.5A2.5 2.5 0 0 0 4 16h8a2.5 2.5 0 0 0 2.5-2.5m-1.5 0v-7H8v-5H3v12a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1M9.5 5V2.12L12.38 5zM5.13 5h-.62v1.25h2.12V5zm-.62 3h7.12v1.25H4.5zm.62 3h-.62v1.25h7.12V11z" clip-rule="evenodd" fill="#666" fill-rule="evenodd"/></svg>
@@ -0,0 +1 @@
1
+ <svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><g clip-path="url(#a)"><path fill-rule="evenodd" clip-rule="evenodd" d="M10.27 14.1a6.5 6.5 0 0 0 3.67-3.45q-1.24.21-2.7.34-.31 1.83-.97 3.1M8 16A8 8 0 1 0 8 0a8 8 0 0 0 0 16m.48-1.52a7 7 0 0 1-.96 0H7.5a4 4 0 0 1-.84-1.32q-.38-.89-.63-2.08a40 40 0 0 0 3.92 0q-.25 1.2-.63 2.08a4 4 0 0 1-.84 1.31zm2.94-4.76q1.66-.15 2.95-.43a7 7 0 0 0 0-2.58q-1.3-.27-2.95-.43a18 18 0 0 1 0 3.44m-1.27-3.54a17 17 0 0 1 0 3.64 39 39 0 0 1-4.3 0 17 17 0 0 1 0-3.64 39 39 0 0 1 4.3 0m1.1-1.17q1.45.13 2.69.34a6.5 6.5 0 0 0-3.67-3.44q.65 1.26.98 3.1M8.48 1.5l.01.02q.41.37.84 1.31.38.89.63 2.08a40 40 0 0 0-3.92 0q.25-1.2.63-2.08a4 4 0 0 1 .85-1.32 7 7 0 0 1 .96 0m-2.75.4a6.5 6.5 0 0 0-3.67 3.44 29 29 0 0 1 2.7-.34q.31-1.83.97-3.1M4.58 6.28q-1.66.16-2.95.43a7 7 0 0 0 0 2.58q1.3.27 2.95.43a18 18 0 0 1 0-3.44m.17 4.71q-1.45-.12-2.69-.34a6.5 6.5 0 0 0 3.67 3.44q-.65-1.27-.98-3.1" fill="#666"/></g><defs><clipPath id="a"><path fill="#fff" d="M0 0h16v16H0z"/></clipPath></defs></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 394 80"><path fill="#000" d="M262 0h68.5v12.7h-27.2v66.6h-13.6V12.7H262V0ZM149 0v12.7H94v20.4h44.3v12.6H94v21h55v12.6H80.5V0h68.7zm34.3 0h-17.8l63.8 79.4h17.9l-32-39.7 32-39.6h-17.9l-23 28.6-23-28.6zm18.3 56.7-9-11-27.1 33.7h17.8l18.3-22.7z"/><path fill="#000" d="M81 79.3 17 0H0v79.3h13.6V17l50.2 62.3H81Zm252.6-.4c-1 0-1.8-.4-2.5-1s-1.1-1.6-1.1-2.6.3-1.8 1-2.5 1.6-1 2.6-1 1.8.3 2.5 1a3.4 3.4 0 0 1 .6 4.3 3.7 3.7 0 0 1-3 1.8zm23.2-33.5h6v23.3c0 2.1-.4 4-1.3 5.5a9.1 9.1 0 0 1-3.8 3.5c-1.6.8-3.5 1.3-5.7 1.3-2 0-3.7-.4-5.3-1s-2.8-1.8-3.7-3.2c-.9-1.3-1.4-3-1.4-5h6c.1.8.3 1.6.7 2.2s1 1.2 1.6 1.5c.7.4 1.5.5 2.4.5 1 0 1.8-.2 2.4-.6a4 4 0 0 0 1.6-1.8c.3-.8.5-1.8.5-3V45.5zm30.9 9.1a4.4 4.4 0 0 0-2-3.3 7.5 7.5 0 0 0-4.3-1.1c-1.3 0-2.4.2-3.3.5-.9.4-1.6 1-2 1.6a3.5 3.5 0 0 0-.3 4c.3.5.7.9 1.3 1.2l1.8 1 2 .5 3.2.8c1.3.3 2.5.7 3.7 1.2a13 13 0 0 1 3.2 1.8 8.1 8.1 0 0 1 3 6.5c0 2-.5 3.7-1.5 5.1a10 10 0 0 1-4.4 3.5c-1.8.8-4.1 1.2-6.8 1.2-2.6 0-4.9-.4-6.8-1.2-2-.8-3.4-2-4.5-3.5a10 10 0 0 1-1.7-5.6h6a5 5 0 0 0 3.5 4.6c1 .4 2.2.6 3.4.6 1.3 0 2.5-.2 3.5-.6 1-.4 1.8-1 2.4-1.7a4 4 0 0 0 .8-2.4c0-.9-.2-1.6-.7-2.2a11 11 0 0 0-2.1-1.4l-3.2-1-3.8-1c-2.8-.7-5-1.7-6.6-3.2a7.2 7.2 0 0 1-2.4-5.7 8 8 0 0 1 1.7-5 10 10 0 0 1 4.3-3.5c2-.8 4-1.2 6.4-1.2 2.3 0 4.4.4 6.2 1.2 1.8.8 3.2 2 4.3 3.4 1 1.4 1.5 3 1.5 5h-5.8z"/></svg>
@@ -0,0 +1 @@
1
+ <svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1155 1000"><path d="m577.3 0 577.4 1000H0z" fill="#fff"/></svg>
@@ -0,0 +1 @@
1
+ <svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill-rule="evenodd" clip-rule="evenodd" d="M1.5 2.5h13v10a1 1 0 0 1-1 1h-11a1 1 0 0 1-1-1zM0 1h16v11.5a2.5 2.5 0 0 1-2.5 2.5h-11A2.5 2.5 0 0 1 0 12.5zm3.75 4.5a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5M7 4.75a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0m1.75.75a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5" fill="#666"/></svg>
@@ -0,0 +1,149 @@
1
+ import fetch from "@/libs/fetch";
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";
16
+
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
+ }
149
+ }
@@ -0,0 +1,5 @@
1
+ const DefaultLayout = ({ children }: { children: React.ReactNode }) => {
2
+ return <div>{children}</div>;
3
+ };
4
+
5
+ export default DefaultLayout;
@@ -0,0 +1,5 @@
1
+ const Page = () => {
2
+ return <div>Page</div>;
3
+ };
4
+
5
+ export default Page;
@@ -0,0 +1,16 @@
1
+ "use client";
2
+ import useUserStore from "@/store/useUserStore";
3
+ import { Button } from "@mantine/core";
4
+ import { useTranslations } from "next-intl";
5
+
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
+ );
14
+ };
15
+
16
+ export default Page;
@@ -0,0 +1,55 @@
1
+ "use client";
2
+ import React, { useEffect } from "react";
3
+ import { signIn } from "next-auth/react";
4
+
5
+ import { Loader } from "@mantine/core";
6
+
7
+ interface IProps {
8
+ isLogin: boolean;
9
+ }
10
+
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
+ }, []);
28
+
29
+ /**
30
+ * 登录事件代理
31
+ */
32
+ const loginHandler = () => {
33
+ const isPro = process.env.NEXT_PUBLIC_NODE_ENV !== "dev";
34
+
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
+ );
53
+ };
54
+
55
+ export default LoginHandle;