create-pardx-scaffold 0.1.6 → 0.1.8

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-pardx-scaffold",
3
- "version": "0.1.6",
3
+ "version": "0.1.8",
4
4
  "description": "Scaffold a new project from PardxAI monorepo (git-tracked files)",
5
5
  "license": "MIT",
6
6
  "bin": "./cli.js",
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Prisma 7.x Configuration File
3
+ *
4
+ * This file is used by Prisma CLI commands (migrate, db push, etc.)
5
+ * For PrismaClient initialization, the URL is passed via the driver adapter
6
+ * in PrismaWriteService and PrismaReadService.
7
+ *
8
+ * 注意: 生成器配置(如 output 路径)需要在 schema.prisma 中配置,
9
+ * 不在此文件中配置。
10
+ *
11
+ * @see https://www.prisma.io/docs/orm/more/upgrade-guides/upgrading-versions/upgrading-to-prisma-7
12
+ */
13
+
14
+ import * as dotenv from 'dotenv';
15
+ import * as dotenvExpand from 'dotenv-expand';
16
+ import { defineConfig, env } from 'prisma/config';
17
+
18
+ // 使用 dotenv-expand 展开环境变量(如 ${BASE_HOST})
19
+ dotenvExpand.expand(dotenv.config());
20
+
21
+ export default defineConfig({
22
+ schema: 'prisma/schema.prisma',
23
+ migrations: {
24
+ path: 'prisma/migrations',
25
+ },
26
+ datasource: {
27
+ url: env('DATABASE_URL'),
28
+ },
29
+ });
@@ -30,7 +30,6 @@ export const namespaces = [
30
30
  'chat',
31
31
  'common',
32
32
  'creative',
33
- 'daily-challenge',
34
33
  'errors',
35
34
  'forms',
36
35
  'memory',
@@ -20,7 +20,6 @@ import type zhCNSettings from '../locales/zh-CN/settings.json';
20
20
  import type zhCNSubscription from '../locales/zh-CN/subscription.json';
21
21
  import type zhCNRecommendation from '../locales/zh-CN/recommendation.json';
22
22
  import type zhCNMemory from '../locales/zh-CN/memory.json';
23
- import type zhCNDailyChallenge from '../locales/zh-CN/daily-challenge.json';
24
23
 
25
24
  /**
26
25
  * 所有翻译消息的类型定义
@@ -39,7 +38,6 @@ export interface AppMessages {
39
38
  subscription: typeof zhCNSubscription;
40
39
  recommendation: typeof zhCNRecommendation;
41
40
  memory: typeof zhCNMemory;
42
- 'daily-challenge': typeof zhCNDailyChallenge;
43
41
  }
44
42
 
45
43
  /**
@@ -49,7 +47,6 @@ export interface AppMessages {
49
47
  declare global {
50
48
  // eslint-disable-next-line @typescript-eslint/no-namespace
51
49
  namespace IntlMessages {
52
- // eslint-disable-next-line @typescript-eslint/no-empty-interface
53
50
  interface Messages extends AppMessages {}
54
51
  }
55
52
  }
@@ -23,6 +23,7 @@
23
23
  import { useEffect } from 'react';
24
24
  import { usePathname, useSearchParams } from 'next/navigation';
25
25
  import { analytics } from '../index';
26
+ import type { EventProperties } from '@repo/contracts/schemas/analytics.schema';
26
27
 
27
28
  interface PageTrackerProps {
28
29
  /**
@@ -56,9 +57,10 @@ export function PageTracker({
56
57
  }
57
58
 
58
59
  // Build full path with search params if enabled
59
- const fullPath = trackSearchParams && searchParams.toString()
60
- ? `${pathname}?${searchParams.toString()}`
61
- : pathname;
60
+ const fullPath =
61
+ trackSearchParams && searchParams.toString()
62
+ ? `${pathname}?${searchParams.toString()}`
63
+ : pathname;
62
64
 
63
65
  // Get page name (custom or pathname)
64
66
  const pageName = pageNameMapper ? pageNameMapper(pathname) : pathname;
@@ -67,10 +69,13 @@ export function PageTracker({
67
69
  analytics.pageView(fullPath, pageName);
68
70
 
69
71
  // Track session start on first page view
70
- if (typeof window !== 'undefined' && !sessionStorage.getItem('session_started')) {
72
+ if (
73
+ typeof window !== 'undefined' &&
74
+ !sessionStorage.getItem('session_started')
75
+ ) {
71
76
  analytics.track('SESSION_START', {
72
77
  path: fullPath,
73
- } as any);
78
+ } as unknown as EventProperties);
74
79
  sessionStorage.setItem('session_started', 'true');
75
80
  }
76
81
  }, [pathname, searchParams, trackSearchParams, pageNameMapper, excludePaths]);
@@ -80,7 +85,7 @@ export function PageTracker({
80
85
  const handleBeforeUnload = () => {
81
86
  analytics.track('SESSION_END', {
82
87
  path: pathname,
83
- } as any);
88
+ } as unknown as EventProperties);
84
89
  };
85
90
 
86
91
  window.addEventListener('beforeunload', handleBeforeUnload);
@@ -96,11 +101,11 @@ export function PageTracker({
96
101
  if (document.hidden) {
97
102
  analytics.track('APP_BACKGROUND', {
98
103
  path: pathname,
99
- } as any);
104
+ } as unknown as EventProperties);
100
105
  } else {
101
106
  analytics.track('APP_FOREGROUND', {
102
107
  path: pathname,
103
- } as any);
108
+ } as unknown as EventProperties);
104
109
  }
105
110
  };
106
111
 
@@ -121,9 +126,6 @@ export const defaultPageNameMapper = (pathname: string): string => {
121
126
  const routes: Record<string, string> = {
122
127
  '/': 'Home',
123
128
  '/home': 'Home',
124
- '/daily-challenge': 'Daily Challenge',
125
- '/daily-challenge/result': 'Challenge Result',
126
- '/daily-challenge/history': 'Challenge History',
127
129
  '/settings': 'Settings',
128
130
  '/login': 'Login',
129
131
  '/register': 'Register',
@@ -32,7 +32,7 @@ export default async function middleware(request: NextRequest) {
32
32
  const pathWithoutLocale = pathname.replace(/^\/(zh-CN|en)/, '') || '/';
33
33
 
34
34
  // Skip daily check-in logic for public routes
35
- const publicRoutes = ['/daily-challenge', '/login', '/register'];
35
+ const publicRoutes = ['/login', '/register'];
36
36
  if (publicRoutes.some((route) => pathWithoutLocale.includes(route))) {
37
37
  return intlResponse;
38
38
  }
@@ -43,46 +43,6 @@ export default async function middleware(request: NextRequest) {
43
43
  return intlResponse;
44
44
  }
45
45
 
46
- // Check today's check-in status
47
- try {
48
- const apiUrl =
49
- process.env.NEXT_PUBLIC_API_BASE_URL || 'http://localhost:3100/api';
50
- const response = await fetch(`${apiUrl}/daily-question/streak`, {
51
- headers: {
52
- Authorization: `Bearer ${token}`,
53
- },
54
- });
55
-
56
- if (response.ok) {
57
- const data = await response.json();
58
- const streak = data.data;
59
-
60
- if (streak?.lastCheckInDate) {
61
- const today = new Date();
62
- today.setHours(0, 0, 0, 0);
63
- const lastCheckIn = new Date(streak.lastCheckInDate);
64
- lastCheckIn.setHours(0, 0, 0, 0);
65
-
66
- // If not checked in today, redirect to daily challenge
67
- if (lastCheckIn.getTime() !== today.getTime()) {
68
- const url = request.nextUrl.clone();
69
- // 保留语言前缀
70
- const locale = pathname.match(/^\/(zh-CN|en)/)?.[1] || 'zh-CN';
71
- url.pathname = `/${locale}/daily-challenge`;
72
- return NextResponse.redirect(url);
73
- }
74
- } else {
75
- // No check-in history, redirect to daily challenge
76
- const url = request.nextUrl.clone();
77
- const locale = pathname.match(/^\/(zh-CN|en)/)?.[1] || 'zh-CN';
78
- url.pathname = `/${locale}/daily-challenge`;
79
- return NextResponse.redirect(url);
80
- }
81
- }
82
- } catch (error) {
83
- console.error('Middleware check-in status error:', error);
84
- }
85
-
86
46
  return intlResponse;
87
47
  }
88
48
 
@@ -43,14 +43,6 @@ export default {
43
43
  );
44
44
  },
45
45
 
46
- escapeMongoRegexSpecialChars(inputString: string): string {
47
- // MongoDB中的正则表达式特殊字符
48
- const mongoRegexSpecialChars = /[\.^$*{[\]|}\\+?\-()]/g;
49
-
50
- // 使用replace函数和回调函数来替换特殊字符
51
- return inputString.replace(mongoRegexSpecialChars, (match) => `\\${match}`);
52
- },
53
-
54
46
  generateString(input: string, suffixLength = 3): string {
55
47
  // 判断是否为中文
56
48
  const isChinese = /^[\u4e00-\u9fa5]+$/.test(input);
@@ -38,6 +38,7 @@ const rl = readline.createInterface({
38
38
  const question = (query) => new Promise((resolve) => rl.question(query, resolve));
39
39
 
40
40
  // Project configuration
41
+ // 注意:API 端口在 apps/api/config.local.yaml 的 app.port 配置;Web 端口由 Next.js 自动配置,此处不配置
41
42
  const config = {
42
43
  projectName: '',
43
44
  projectDescription: '',
@@ -48,8 +49,6 @@ const config = {
48
49
  redisUrl: '',
49
50
  rabbitmqUrl: '',
50
51
  baseHost: '127.0.0.1',
51
- apiPort: '',
52
- webPort: '',
53
52
  };
54
53
 
55
54
  async function main() {
@@ -84,16 +83,6 @@ async function main() {
84
83
 
85
84
  log.header('\n📦 Configuration');
86
85
 
87
- config.apiPort = await question(
88
- `${colors.cyan}API port${colors.reset} [3100]: `
89
- );
90
- config.apiPort = config.apiPort.trim() || '3100';
91
-
92
- config.webPort = await question(
93
- `${colors.cyan}Web port${colors.reset} [3000]: `
94
- );
95
- config.webPort = config.webPort.trim() || '3000';
96
-
97
86
  config.databaseUrl = await question(
98
87
  `${colors.cyan}Database URL${colors.reset} [postgresql://user:password@localhost:5432/dbname]: `
99
88
  );
@@ -126,8 +115,6 @@ async function main() {
126
115
  console.log(` Project Name: ${colors.green}${config.projectName}${colors.reset}`);
127
116
  console.log(` Description: ${config.projectDescription}`);
128
117
  console.log(` Author: ${config.authorName} <${config.authorEmail}>`);
129
- console.log(` API Port: ${config.apiPort}`);
130
- console.log(` Web Port: ${config.webPort}`);
131
118
  console.log(` Base Host: ${config.baseHost}`);
132
119
  console.log(` Database: ${config.databaseUrl}`);
133
120
  console.log(` Read DB: ${config.readDatabaseUrl}`);
@@ -237,14 +224,13 @@ function createEnvFromExample(rootDir, config) {
237
224
  const webExamplePath = path.join(rootDir, 'apps/web/.env.example');
238
225
 
239
226
  // 变量替换映射(apps/api),参考 apps/api/.env.example
227
+ // API 端口由 apps/api/config.local.yaml 的 app.port 配置,此处不覆盖 API_BASE_URL / INTERNAL_API_BASE_URL
240
228
  const apiReplacements = {
241
229
  BASE_HOST: config.baseHost,
242
230
  DATABASE_URL: config.databaseUrl,
243
231
  READ_DATABASE_URL: config.readDatabaseUrl,
244
232
  REDIS_URL: config.redisUrl,
245
233
  RABBITMQ_URL: config.rabbitmqUrl,
246
- API_BASE_URL: `http://localhost:${config.apiPort}/api`,
247
- INTERNAL_API_BASE_URL: `http://127.0.0.1:${config.apiPort}/api`,
248
234
  };
249
235
 
250
236
  if (fs.existsSync(apiExamplePath)) {
@@ -281,15 +267,11 @@ function createEnvFromExample(rootDir, config) {
281
267
  REDIS_URL: config.redisUrl,
282
268
  RABBITMQ_URL: config.rabbitmqUrl,
283
269
  JWT_SECRET: generateRandomSecret(),
284
- API_BASE_URL: `http://localhost:${config.apiPort}/api`,
285
- INTERNAL_API_BASE_URL: `http://127.0.0.1:${config.apiPort}/api`,
286
270
  });
287
271
  }
288
272
 
289
- // apps/web
290
- const webReplacements = {
291
- NEXT_PUBLIC_API_BASE_URL: `http://localhost:${config.apiPort}/api`,
292
- };
273
+ // apps/web:NEXT_PUBLIC_API_BASE_URL 等由 .env.example 提供,Next.js 端口自动配置,此处不覆盖
274
+ const webReplacements = {};
293
275
 
294
276
  if (fs.existsSync(webExamplePath)) {
295
277
  const content = fs.readFileSync(webExamplePath, 'utf8');
@@ -309,9 +291,7 @@ function createEnvFromExample(rootDir, config) {
309
291
  outLines.join('\n') + '\n'
310
292
  );
311
293
  } else {
312
- createEnvFile(path.join(rootDir, 'apps/web/.env.local'), {
313
- NEXT_PUBLIC_API_BASE_URL: `http://localhost:${config.apiPort}/api`,
314
- });
294
+ createEnvFile(path.join(rootDir, 'apps/web/.env.local'), {});
315
295
  }
316
296
  }
317
297