@stdex/create 0.0.0-stage → 1.1.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.
- package/LICENSE +21 -0
- package/README.md +66 -2
- package/README_EN.md +67 -0
- package/package.json +39 -3
- package/src/eslint.config.js +2 -0
- package/src/index.js +232 -0
- package/templates/base/eslint.config.js +2 -0
- package/templates/base/index.js +1 -0
- package/templates/base/package.json +16 -0
- package/templates/miniprogram/README.md +27 -0
- package/templates/miniprogram/_gitignore +3 -0
- package/templates/miniprogram/dist/app.js +9 -0
- package/templates/miniprogram/dist/app.json +16 -0
- package/templates/miniprogram/dist/app.wxss +0 -0
- package/templates/miniprogram/dist/lib/app-data.js +51 -0
- package/templates/miniprogram/dist/pages/index/index.js +20 -0
- package/templates/miniprogram/dist/pages/index/index.json +3 -0
- package/templates/miniprogram/dist/pages/index/index.wxml +1 -0
- package/templates/miniprogram/dist/pages/index/index.wxss +11 -0
- package/templates/miniprogram/dist/sitemap.json +9 -0
- package/templates/miniprogram/eslint.config.cjs +25 -0
- package/templates/miniprogram/gulpfile.js +81 -0
- package/templates/miniprogram/package.json +22 -0
- package/templates/miniprogram/project.config.json +31 -0
- package/templates/miniprogram/project.private.config.json +8 -0
- package/templates/miniprogram/src/app.json +16 -0
- package/templates/miniprogram/src/app.scss +0 -0
- package/templates/miniprogram/src/app.ts +6 -0
- package/templates/miniprogram/src/lib/app-data.ts +46 -0
- package/templates/miniprogram/src/pages/index/index.json +3 -0
- package/templates/miniprogram/src/pages/index/index.scss +11 -0
- package/templates/miniprogram/src/pages/index/index.ts +13 -0
- package/templates/miniprogram/src/pages/index/index.wxml +1 -0
- package/templates/miniprogram/src/sitemap.json +9 -0
- package/templates/miniprogram/tsconfig.json +11 -0
- package/templates/miniprogram/typings/index.d.ts +5 -0
- package/templates/miniprogram/typings/types/index.d.ts +1 -0
- package/templates/miniprogram/typings/types/wx/index.d.ts +162 -0
- package/templates/miniprogram/typings/types/wx/lib.wx.api.d.ts +32387 -0
- package/templates/miniprogram/typings/types/wx/lib.wx.app.d.ts +400 -0
- package/templates/miniprogram/typings/types/wx/lib.wx.behavior.d.ts +68 -0
- package/templates/miniprogram/typings/types/wx/lib.wx.canvas.d.ts +2623 -0
- package/templates/miniprogram/typings/types/wx/lib.wx.cloud.d.ts +1016 -0
- package/templates/miniprogram/typings/types/wx/lib.wx.component.d.ts +700 -0
- package/templates/miniprogram/typings/types/wx/lib.wx.event.d.ts +1436 -0
- package/templates/miniprogram/typings/types/wx/lib.wx.page.d.ts +272 -0
- package/templates/miniprogram/typings/types/wx/lib.wx.phys3D.d.ts +409 -0
- package/templates/miniprogram/typings/types/wx/lib.wx.wasm.d.ts +152 -0
- package/templates/miniprogram/typings/types/wx/lib.wx.xr-frame.d.ts +15631 -0
- package/templates/miniprogram/variables.json +11 -0
- package/templates/service/.env +1 -0
- package/templates/service/README.md +64 -0
- package/templates/service/_gitignore +5 -0
- package/templates/service/eslint.config.js +2 -0
- package/templates/service/package.json +44 -0
- package/templates/service/prisma/schema.prisma +40 -0
- package/templates/service/prisma/sql/demoTypedSql.sql +1 -0
- package/templates/service/prisma.config.ts +12 -0
- package/templates/service/setup.js +4 -0
- package/templates/service/src/cache.ts +28 -0
- package/templates/service/src/config.ts +49 -0
- package/templates/service/src/controllers/common.ts +43 -0
- package/templates/service/src/controllers/demo-school.ts +7 -0
- package/templates/service/src/controllers/demo-student.ts +7 -0
- package/templates/service/src/controllers/index.ts +15 -0
- package/templates/service/src/database.ts +52 -0
- package/templates/service/src/index.ts +15 -0
- package/templates/service/src/logger.ts +11 -0
- package/templates/service/src/routes/demo-school.ts +9 -0
- package/templates/service/src/routes/index.ts +3 -0
- package/templates/service/src/routes/router.ts +42 -0
- package/templates/service/src/tasks/common.ts +24 -0
- package/templates/service/src/tasks/demo-school.ts +6 -0
- package/templates/service/src/tasks/index.ts +1 -0
- package/templates/service/tsconfig.build.json +9 -0
- package/templates/service/tsconfig.json +10 -0
- package/templates/site/_gitignore +3 -0
- package/templates/site/_pnpm-workspace.yaml +4 -0
- package/templates/site/eslint.config.js +19 -0
- package/templates/site/package.json +38 -0
- package/templates/site/public/favicon.png +0 -0
- package/templates/site/src/app/header.tsx +31 -0
- package/templates/site/src/app/index.tsx +38 -0
- package/templates/site/src/app/layout.tsx +63 -0
- package/templates/site/src/app/menu.tsx +115 -0
- package/templates/site/src/app/router.tsx +50 -0
- package/templates/site/src/app/style.css +9 -0
- package/templates/site/src/assets/logo.png +0 -0
- package/templates/site/src/components/Breadcrumb.tsx +47 -0
- package/templates/site/src/components/DevStatus.tsx +14 -0
- package/templates/site/src/index.html +13 -0
- package/templates/site/src/index.tsx +5 -0
- package/templates/site/src/lib/config.ts +6 -0
- package/templates/site/src/lib/logging.ts +11 -0
- package/templates/site/src/views/dashboard.tsx +7 -0
- package/templates/site/src/views/school-detail.tsx +13 -0
- package/templates/site/src/views/school-ranking.tsx +7 -0
- package/templates/site/src/views/schools.tsx +43 -0
- package/templates/site/src/views/setting.tsx +7 -0
- package/templates/site/src/views/student-score.tsx +7 -0
- package/templates/site/src/views/students.tsx +7 -0
- package/templates/site/tsconfig.json +10 -0
- package/templates/site/vite.config.js +60 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
DB_URL=postgresql://localhost
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
服务端项目
|
|
2
|
+
|
|
3
|
+
# 架构设计
|
|
4
|
+
|
|
5
|
+
```yaml
|
|
6
|
+
Schema: 定义业务实体
|
|
7
|
+
Controller: 封装业务逻辑,包括对业务实体的查询和操作
|
|
8
|
+
Task: 执行定期任务
|
|
9
|
+
Route: 处理请求(检查传参、格式化响应内容、触发相应的业务逻辑...)
|
|
10
|
+
Module: 通用组件或对某一特定功能的封装
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
代码中:
|
|
14
|
+
|
|
15
|
+
- `prisma/` 对应 `Schema`
|
|
16
|
+
- `src/controllers/` 对应 `Controller`
|
|
17
|
+
- `src/tasks/` 对应 `Task`
|
|
18
|
+
- `src/routes/` 对应 `Route`
|
|
19
|
+
- `src/` 下的其他内容都是 `Module`
|
|
20
|
+
|
|
21
|
+
routes 和 controller 都会验证数据有效性。但分工有所区别:
|
|
22
|
+
|
|
23
|
+
- routes 主要验证“格式”上的有效性
|
|
24
|
+
- controller 负责验证“业务逻辑”上的有效性,它假定使用者传入的数据格式一定是合法的。
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
# Prisma ORM 适配
|
|
29
|
+
|
|
30
|
+
## 约定
|
|
31
|
+
|
|
32
|
+
1. prisma 定义(schema、migrations...)放在 `prisma/`,此为 prisma 默认支持的位置。
|
|
33
|
+
2. prisma 生成的 client 代码放在 `src/prisma` 以便于引用。
|
|
34
|
+
3. `prisma` 命令会从环境变量或 `.env` 文件中获取数据库连接参数。
|
|
35
|
+
因此此项目设计为用 `.env` 文件维护配置,使得代码和 `prisma` 命令都能方便地读取到需要的配置值。
|
|
36
|
+
|
|
37
|
+
## 本地开发
|
|
38
|
+
|
|
39
|
+
1. 每次修改 schema 后,执行 `prisma db push`,把修改同步到数据库中。此操作会顺便重新生成 client 代码。
|
|
40
|
+
或者执行 `prisma generate` 只生成 client 代码,不同步数据库(针对本机开发没有对应的数据库或不希望写入数据库的情况)。
|
|
41
|
+
2. 每次修改 TypedSQL 后,执行 `prisma generate --sql` 更新 SQL 类型定义。
|
|
42
|
+
3. 开发完成后,执行 `prisma migrate dev` 来生成一个 `migration`。
|
|
43
|
+
注意:因为之前执行了 `prisma db push`,生成 migrate 时会因为表结构冲突要求重置整个数据库、清除数据,相关问题:<https://github.com/prisma/prisma/discussions/16141>。
|
|
44
|
+
目前能想到的解决办法是备份数据(不备份表结构),等生成 migrations 后再导入回来。
|
|
45
|
+
|
|
46
|
+
前两项操作已定义成 `pnpm db-push` 或 `pnpm db-generate`,
|
|
47
|
+
第三项已定义成 `pnpm db-save`。
|
|
48
|
+
|
|
49
|
+
## 生产环境运行
|
|
50
|
+
|
|
51
|
+
初始化或每次数据库有变动时:
|
|
52
|
+
|
|
53
|
+
1. 执行 `pnpm prisma generate` 生成 client 代码。
|
|
54
|
+
2. 执行 `pnpm prisma migrate deploy` 来应用 migrations。
|
|
55
|
+
|
|
56
|
+
此操作已整合成 `pnpm db-deploy` 命令。
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
# 缓存使用流程
|
|
61
|
+
|
|
62
|
+
1. 确定要缓存的内容,通过 `getCacheInstance()` 建立缓存管理器。
|
|
63
|
+
2. 读取缓存时,若对应内容不存在,获取数据并存入缓存
|
|
64
|
+
3. 相关数据更新时,清除缓存,待下一次读取时重新写入
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"private": true,
|
|
3
|
+
"name": "project",
|
|
4
|
+
"description": "服务端项目",
|
|
5
|
+
"version": "1.0.0",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"build": "rm -rf dist && tsc-with-alias --project tsconfig.build.json",
|
|
9
|
+
"watch": "rm -rf dist && tsc-with-alias --watch --project tsconfig.build.json",
|
|
10
|
+
"dev": "rm -rf dist && tsc-with-alias --watch --project tsconfig.build.json --nodemon=dist/index.js",
|
|
11
|
+
"start": "node dist/index.js",
|
|
12
|
+
"lint": "tsc --noEmit && eslint './src/'",
|
|
13
|
+
"db-generate": "prisma generate --sql",
|
|
14
|
+
"db-push": "prisma db push",
|
|
15
|
+
"db-save": "prisma migrate dev",
|
|
16
|
+
"db-deploy": "prisma generate && prisma migrate deploy"
|
|
17
|
+
},
|
|
18
|
+
"dependencies": {
|
|
19
|
+
"@stdex/utils": "latest",
|
|
20
|
+
"@prisma/adapter-pg": "latest",
|
|
21
|
+
"@prisma/client": "latest",
|
|
22
|
+
"dayjs": "latest",
|
|
23
|
+
"dotenv": "latest",
|
|
24
|
+
"lodash": "latest",
|
|
25
|
+
"pg": "latest",
|
|
26
|
+
"redis": "latest",
|
|
27
|
+
"starlight-server": "latest"
|
|
28
|
+
},
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"@stdex/presets-eslint-node": "latest",
|
|
31
|
+
"@stdex/presets-prettier": "latest",
|
|
32
|
+
"@stdex/presets-typescript": "latest",
|
|
33
|
+
"@types/lodash": "latest",
|
|
34
|
+
"@types/node": "latest",
|
|
35
|
+
"@types/pg": "latest",
|
|
36
|
+
"concurrently": "latest",
|
|
37
|
+
"eslint": "latest",
|
|
38
|
+
"nodemon": "latest",
|
|
39
|
+
"prisma": "latest",
|
|
40
|
+
"tsc-alias": "latest",
|
|
41
|
+
"typescript": "^6.0.3"
|
|
42
|
+
},
|
|
43
|
+
"prettier": "@stdex/presets-prettier/prettierrc"
|
|
44
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
// 数据库设计约定见 README.md
|
|
2
|
+
|
|
3
|
+
// -------------------------------
|
|
4
|
+
// 数据库配置
|
|
5
|
+
// -------------------------------
|
|
6
|
+
|
|
7
|
+
datasource db {
|
|
8
|
+
provider = "postgresql"
|
|
9
|
+
extensions = [citext]
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
generator client {
|
|
13
|
+
provider = "prisma-client"
|
|
14
|
+
previewFeatures = ["postgresqlExtensions", "typedSql"]
|
|
15
|
+
output = "../src/prisma/"
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// -------------------------------
|
|
19
|
+
// 业务实体
|
|
20
|
+
// -------------------------------
|
|
21
|
+
|
|
22
|
+
model School {
|
|
23
|
+
id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid
|
|
24
|
+
name String @unique @db.Citext
|
|
25
|
+
|
|
26
|
+
students Student[]
|
|
27
|
+
|
|
28
|
+
@@map("schools")
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
model Student {
|
|
32
|
+
id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid
|
|
33
|
+
name String @db.Citext
|
|
34
|
+
deleteTime DateTime? @map("delete_time") @db.Timestamptz()
|
|
35
|
+
|
|
36
|
+
school School @relation(fields: [schoolId], references: [id])
|
|
37
|
+
schoolId String @map("school_id") @db.Uuid
|
|
38
|
+
|
|
39
|
+
@@map("students")
|
|
40
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
SELECT * FROM schools;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 维护基于 redis 的数据缓存
|
|
3
|
+
*/
|
|
4
|
+
import { type CacheOptions, Cache, initRedisLogging } from '@stdex/utils/env-service/redis-cache.js'
|
|
5
|
+
import { createClient, type RedisClientType } from 'redis'
|
|
6
|
+
import config from '@/config.js'
|
|
7
|
+
import { rootLogger } from '@/logger.js'
|
|
8
|
+
|
|
9
|
+
export const redis: RedisClientType = createClient({
|
|
10
|
+
url: config.REDIS_URL,
|
|
11
|
+
disableOfflineQueue: true, // 若在 Redis 连接断开时执行命令,立刻失败,而不是等待重新连接
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
export async function initRedis() {
|
|
15
|
+
initRedisLogging(redis)
|
|
16
|
+
await redis.connect()
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* 生成管理某项缓存的实例
|
|
21
|
+
* 使用规范见 README.md 里的“缓存使用流程”
|
|
22
|
+
*/
|
|
23
|
+
export function getCacheInstance<T>(topic: string, options?: Partial<CacheOptions>) {
|
|
24
|
+
return new Cache<T>(redis, config.REDIS_KEY_PREFIX + topic, {
|
|
25
|
+
logger: rootLogger.getChild('cache'),
|
|
26
|
+
...(options ?? {}),
|
|
27
|
+
})
|
|
28
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import path from 'node:path'
|
|
2
|
+
import { getDirectoryPath } from '@stdex/utils/env-node/index.js'
|
|
3
|
+
import { EnvReader } from '@stdex/utils/env-service/index.js'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* 环境信息
|
|
7
|
+
*/
|
|
8
|
+
const dirpath = getDirectoryPath(import.meta.url)
|
|
9
|
+
export const appRoot = path.resolve(dirpath, '../')
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* 业务常量
|
|
13
|
+
*/
|
|
14
|
+
export const constants = {
|
|
15
|
+
// 此处定义业务常量
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* 设置
|
|
20
|
+
* 通过 .env 文件覆盖设置
|
|
21
|
+
*/
|
|
22
|
+
const envReader = new EnvReader({
|
|
23
|
+
path: [path.join(appRoot, '.env')],
|
|
24
|
+
})
|
|
25
|
+
const config = envReader.batchGet({
|
|
26
|
+
// ---------- 基础设置 ----------
|
|
27
|
+
|
|
28
|
+
// 是否开启调试模式,会影响日志记录
|
|
29
|
+
DEBUG: false,
|
|
30
|
+
|
|
31
|
+
// 是否额外开启 Prisma 的调试日志(仅限 DEBUG 为 true 时生效)
|
|
32
|
+
DB_DEBUG: false,
|
|
33
|
+
|
|
34
|
+
// 监听端口
|
|
35
|
+
PORT: 8000,
|
|
36
|
+
|
|
37
|
+
// 日志文件存放路径
|
|
38
|
+
LOGS_DIR: path.join(appRoot, 'logs'),
|
|
39
|
+
|
|
40
|
+
// ---------- 服务连接 ----------
|
|
41
|
+
|
|
42
|
+
// postgresql://[[username][:password]@][host][:port][/db-name]
|
|
43
|
+
DB_URL: '',
|
|
44
|
+
|
|
45
|
+
// redis[s]://[[username][:password]@][host][:port][/db-number]
|
|
46
|
+
REDIS_URL: '',
|
|
47
|
+
REDIS_KEY_PREFIX: '',
|
|
48
|
+
})
|
|
49
|
+
export default config
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type ControllersFrom,
|
|
3
|
+
type AnyController,
|
|
4
|
+
Controller,
|
|
5
|
+
initializeControllers,
|
|
6
|
+
} from '@stdex/utils/env-service/index.js'
|
|
7
|
+
import { getTransactionContextedPrismaClient, type AppPrismaClient } from '@/database.js'
|
|
8
|
+
import { type Logger, rootLogger } from '@/logger.js'
|
|
9
|
+
|
|
10
|
+
export interface Context {
|
|
11
|
+
/**
|
|
12
|
+
* controller 会绑定一个由 getTransactionContextedPrismaClient() 返回的 PrismaClient,它会自动关联当前进行的事务。
|
|
13
|
+
* 应始终使用它而不是全局的 PrismaClient 以保证在正确的事务内执行操作。
|
|
14
|
+
*/
|
|
15
|
+
db: AppPrismaClient
|
|
16
|
+
|
|
17
|
+
baseLogger: Logger
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export class AppController<
|
|
21
|
+
AllControllers extends Record<string, AnyController<Context>>,
|
|
22
|
+
> extends Controller<Context, AllControllers> {
|
|
23
|
+
protected readonly db: AppPrismaClient
|
|
24
|
+
protected readonly logger: Logger
|
|
25
|
+
|
|
26
|
+
constructor(controllers: AllControllers, context: Context, name?: string) {
|
|
27
|
+
super(controllers, context, name)
|
|
28
|
+
this.db = context.db
|
|
29
|
+
this.logger = context.baseLogger.getChild(this.name)
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export type { ControllersFrom }
|
|
34
|
+
|
|
35
|
+
export function initializeAppControllers<T extends Record<string, unknown>>(
|
|
36
|
+
controllerClasses: T,
|
|
37
|
+
db?: AppPrismaClient,
|
|
38
|
+
baseLogger?: Logger,
|
|
39
|
+
) {
|
|
40
|
+
db ??= getTransactionContextedPrismaClient()
|
|
41
|
+
baseLogger ??= rootLogger.getChild('controller')
|
|
42
|
+
return initializeControllers<Context, T>(controllerClasses, { db, baseLogger })
|
|
43
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AppController,
|
|
3
|
+
initializeAppControllers,
|
|
4
|
+
type ControllersFrom,
|
|
5
|
+
type Context,
|
|
6
|
+
} from './common.js'
|
|
7
|
+
import { DemoSchoolController } from './demo-school.js'
|
|
8
|
+
import { DemoStudentController } from './demo-student.js'
|
|
9
|
+
|
|
10
|
+
export { AppController, initializeAppControllers }
|
|
11
|
+
export type Controllers = ControllersFrom<Context, typeof controllerClasses>
|
|
12
|
+
export const controllerClasses = {
|
|
13
|
+
student: DemoStudentController,
|
|
14
|
+
school: DemoSchoolController,
|
|
15
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getPrismaLoggingOptions,
|
|
3
|
+
adaptPrismaLogging,
|
|
4
|
+
exists,
|
|
5
|
+
findAndCount,
|
|
6
|
+
softDelete,
|
|
7
|
+
withTransaction,
|
|
8
|
+
adaptPrismaDebugLogging,
|
|
9
|
+
getTransactionContextedPrismaClient as rawGetTransactionContextedPrismaClient,
|
|
10
|
+
type GetPrismaClientInTransaction,
|
|
11
|
+
} from '@stdex/utils/env-service/prisma/index.js'
|
|
12
|
+
import { PrismaPg } from '@prisma/adapter-pg'
|
|
13
|
+
import config from '@/config.js'
|
|
14
|
+
import { rootLogger } from '@/logger.js'
|
|
15
|
+
import { PrismaClient } from '@/prisma/client.js'
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* 导出相关类型和工具函数
|
|
19
|
+
*/
|
|
20
|
+
export * from '@/prisma/client.js'
|
|
21
|
+
export type AppPrismaClient = typeof prisma
|
|
22
|
+
export type PrismaClientInTransaction = GetPrismaClientInTransaction<AppPrismaClient>
|
|
23
|
+
|
|
24
|
+
const logger = rootLogger.getChild('prisma')
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* 初始化基础 PrismaClient 实例
|
|
28
|
+
*/
|
|
29
|
+
if (config.DEBUG && config.DB_DEBUG) {
|
|
30
|
+
adaptPrismaDebugLogging(logger)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const adapter = new PrismaPg({ connectionString: config.DB_URL })
|
|
34
|
+
export const barePrisma = new PrismaClient({
|
|
35
|
+
adapter,
|
|
36
|
+
...getPrismaLoggingOptions(config.DEBUG ? 'debug' : 'info'),
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
adaptPrismaLogging(barePrisma, rootLogger.getChild('prisma'))
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* 应用扩展
|
|
43
|
+
*/
|
|
44
|
+
export const prisma = barePrisma
|
|
45
|
+
.$extends(softDelete)
|
|
46
|
+
.$extends(withTransaction)
|
|
47
|
+
.$extends(exists)
|
|
48
|
+
.$extends(findAndCount)
|
|
49
|
+
|
|
50
|
+
export function getTransactionContextedPrismaClient() {
|
|
51
|
+
return rawGetTransactionContextedPrismaClient(prisma)
|
|
52
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { startHTTPServer } from 'starlight-server'
|
|
2
|
+
import { initRedis } from '@/cache.js'
|
|
3
|
+
import config from '@/config.js'
|
|
4
|
+
import { rootLogger } from '@/logger.js'
|
|
5
|
+
import { router } from '@/routes/index.js'
|
|
6
|
+
|
|
7
|
+
import './tasks/index.js'
|
|
8
|
+
|
|
9
|
+
await initRedis()
|
|
10
|
+
|
|
11
|
+
startHTTPServer({
|
|
12
|
+
handler: router.handle,
|
|
13
|
+
logger: rootLogger.getChild('http'),
|
|
14
|
+
port: config.PORT,
|
|
15
|
+
})
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { logger as rootLogger } from '@stdex/utils/env-node/logging/index.js'
|
|
2
|
+
import { Logger, initLogger } from 'starlight-server'
|
|
3
|
+
import config from '@/config.js'
|
|
4
|
+
|
|
5
|
+
export { Logger, rootLogger }
|
|
6
|
+
|
|
7
|
+
initLogger(rootLogger, {
|
|
8
|
+
level: config.DEBUG ? 'debug' : 'info',
|
|
9
|
+
debugLib: '*',
|
|
10
|
+
file: { dir: config.LOGS_DIR },
|
|
11
|
+
})
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { Router } from 'starlight-server'
|
|
2
|
+
import {
|
|
3
|
+
initializeAppControllers,
|
|
4
|
+
controllerClasses,
|
|
5
|
+
type Controllers,
|
|
6
|
+
} from '@/controllers/index.js'
|
|
7
|
+
import { getTransactionContextedPrismaClient, type AppPrismaClient } from '@/database.js'
|
|
8
|
+
import { rootLogger } from '@/logger.js'
|
|
9
|
+
export const logger = rootLogger.getChild('API')
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* -----------------------------
|
|
13
|
+
* 初始化、自定义 router
|
|
14
|
+
* -----------------------------
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
declare module 'starlight-server' {
|
|
18
|
+
interface Context {
|
|
19
|
+
db: AppPrismaClient
|
|
20
|
+
controllers: Controllers
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export const router = new Router()
|
|
25
|
+
router.setCors(true)
|
|
26
|
+
router.setExecutor(async (basicContext, route) => {
|
|
27
|
+
// 初始化业务对象
|
|
28
|
+
// 此为和 controllers 里共用的 contexted prisma client。
|
|
29
|
+
// 应始终使用它而不是全局的 PrismaClient 以保证在正确的事务内执行操作。
|
|
30
|
+
const db = getTransactionContextedPrismaClient()
|
|
31
|
+
const controllers = initializeAppControllers(controllerClasses, db)
|
|
32
|
+
|
|
33
|
+
// 初始化 route context 并调用 route handler
|
|
34
|
+
await route.handler({ ...basicContext, db, controllers })
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
router.register({
|
|
38
|
+
path: '/check-health',
|
|
39
|
+
handler({ response }) {
|
|
40
|
+
response.text('ok')
|
|
41
|
+
},
|
|
42
|
+
})
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { TaskManager } from '@stdex/utils/env-service/index.js'
|
|
2
|
+
import {
|
|
3
|
+
type Controllers,
|
|
4
|
+
controllerClasses,
|
|
5
|
+
initializeAppControllers,
|
|
6
|
+
} from '@/controllers/index.js'
|
|
7
|
+
import { getTransactionContextedPrismaClient, type AppPrismaClient } from '@/database.js'
|
|
8
|
+
|
|
9
|
+
interface Context {
|
|
10
|
+
db: AppPrismaClient
|
|
11
|
+
controllers: Controllers
|
|
12
|
+
}
|
|
13
|
+
class AppTaskManager extends TaskManager<Context> {
|
|
14
|
+
getContext(name: string) {
|
|
15
|
+
const db = getTransactionContextedPrismaClient()
|
|
16
|
+
const controllersLogger = this.baseLogger.getChild(name).getChild('controllers')
|
|
17
|
+
const controllers = initializeAppControllers(controllerClasses, db, controllersLogger)
|
|
18
|
+
return {
|
|
19
|
+
db,
|
|
20
|
+
controllers,
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
export const tasks = new AppTaskManager()
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import './demo-school.js'
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import configs from '@stdex/presets-eslint-react'
|
|
2
|
+
export default [
|
|
3
|
+
...configs,
|
|
4
|
+
{
|
|
5
|
+
rules: {
|
|
6
|
+
// 适配 Emotion CSS
|
|
7
|
+
'react/no-unknown-property': ['error', { ignore: ['css'] }], // 适配 Emotion CSS
|
|
8
|
+
|
|
9
|
+
// 适配 @stdex/utils 中的 useAsyncEffect
|
|
10
|
+
'ts-react-hooks/exhaustive-deps': [
|
|
11
|
+
'warn',
|
|
12
|
+
{
|
|
13
|
+
additionalHooks: '(useAsyncEffect)',
|
|
14
|
+
allowAsyncHooks: '(useAsyncEffect)',
|
|
15
|
+
},
|
|
16
|
+
],
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
]
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"private": true,
|
|
3
|
+
"name": "project",
|
|
4
|
+
"version": "1.0.0",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"dev": "vite",
|
|
8
|
+
"build": "vite build",
|
|
9
|
+
"lint": "tsc --noEmit && eslint './src/'"
|
|
10
|
+
},
|
|
11
|
+
"prettier": "@stdex/presets-prettier/prettierrc",
|
|
12
|
+
"dependencies": {
|
|
13
|
+
"@stdex/utils": "latest",
|
|
14
|
+
"@ant-design/cssinjs": "latest",
|
|
15
|
+
"@ant-design/icons": "latest",
|
|
16
|
+
"@emotion/react": "latest",
|
|
17
|
+
"antd": "latest",
|
|
18
|
+
"dayjs": "latest",
|
|
19
|
+
"lodash": "latest",
|
|
20
|
+
"react": "latest",
|
|
21
|
+
"react-dom": "latest",
|
|
22
|
+
"react-router": "latest"
|
|
23
|
+
},
|
|
24
|
+
"devDependencies": {
|
|
25
|
+
"@stdex/presets-eslint-react": "latest",
|
|
26
|
+
"@stdex/presets-prettier": "latest",
|
|
27
|
+
"@stdex/presets-typescript": "latest",
|
|
28
|
+
"@swc/plugin-emotion": "latest",
|
|
29
|
+
"@types/lodash": "latest",
|
|
30
|
+
"@types/node": "latest",
|
|
31
|
+
"@types/react": "latest",
|
|
32
|
+
"@types/react-dom": "latest",
|
|
33
|
+
"@vitejs/plugin-react-swc": "latest",
|
|
34
|
+
"eslint": "latest",
|
|
35
|
+
"vite": "latest",
|
|
36
|
+
"vite-plugin-svgr": "latest"
|
|
37
|
+
}
|
|
38
|
+
}
|
|
Binary file
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import ImageLogo from '@/assets/logo.png'
|
|
2
|
+
import { DevStatus } from '@/components/DevStatus'
|
|
3
|
+
|
|
4
|
+
export function AppHeader() {
|
|
5
|
+
return <div css={s.wrap}>
|
|
6
|
+
<img css={s.logo} src={ImageLogo} />
|
|
7
|
+
<div css={s.siteName}>演示项目</div>
|
|
8
|
+
<DevStatus />
|
|
9
|
+
</div>
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const s = {
|
|
13
|
+
wrap: css`
|
|
14
|
+
align-items: center;
|
|
15
|
+
display: flex;
|
|
16
|
+
padding: 8px 24px;
|
|
17
|
+
`,
|
|
18
|
+
|
|
19
|
+
logo: css`
|
|
20
|
+
height: 42px;
|
|
21
|
+
width: 42px;
|
|
22
|
+
margin-right: 20px;
|
|
23
|
+
`,
|
|
24
|
+
|
|
25
|
+
siteName: css`
|
|
26
|
+
flex: 1;
|
|
27
|
+
font-size: 16px;
|
|
28
|
+
letter-spacing: 1px;
|
|
29
|
+
white-space: nowrap;
|
|
30
|
+
`,
|
|
31
|
+
}
|