This commit is contained in:
longdayi 2024-12-30 09:22:38 +08:00
parent c8dfb4db25
commit 124e263f78
41 changed files with 12611 additions and 462 deletions

View File

@ -6,3 +6,4 @@ dist
test
.md
volumes
*.tar

282
.gitignore vendored Normal file → Executable file
View File

@ -1,235 +1,69 @@
# Node.js
node_modules/
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
backup
# dependencies
**/node_modules/
volumes
/.pnp
.pnp.js
*.tar
# testing
**/coverage/
.env
docker-compose.yml
packages/common/prisma/migrations
packages/common/src/generated
# production
**/build/
**/dist/
# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
# Logs
logs/
*.log
npm-debug.log*
yarn-debug.log*
pnpm-debug.log*
lerna-debug.log*
# 快速刷新错误记录
.expo/web/cache/development/
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
# Expo
**/.expo/
**/.expo-shared/
# Runtime data
pids/
*.pid
*.seed
*.pid.lock
# Android
*.apk
*.aar
*.jks
!apps/mobile/android/app/release.jks
**/android/.gradle/
**/android/app/build/
**/android/app/release/
**/android/react-native-jsc/build/
**/android/react/build/
**/android/*/google-services.json
**/android/app/src/debug/res/xml/react_native_debug.xml
**/android/app/src/dev19/res/xml/react_native_debug.xml
**/android/app/src/dev20/res/xml/react_native_debug.xml
**/android/app/src/main/assets/shell-app.bundle
**/android/app/src/main/res/raw/shell_app_bundle
**/android/app/src/release/res/xml/react_native_debug.xml
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov/
# iOS
**/ios/Pods/
/ios/*.xcworkspace
**/ios/DerivedData/
**/ios/build/
**/ios/Podfile.lock
# Coverage directory used by tools like istanbul
coverage/
*.lcov
# nyc test coverage
.nyc_output/
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt/
# Bower dependency directory (https://bower.io/)
bower_components/
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release/
# Dependency directories
jspm_packages/
# TypeScript v1 declaration files
typings/
# Optional npm cache directory
.npm/
# Optional eslint cache
.eslintcache
# Optional stylelint cache
.stylelintcache
# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env
.env.*
!.env.example
# Parcel-bundler cache (https://parceljs.org/)
.cache
.parcel-cache
# Next.js build output
.next/
out/
# Nuxt.js build / generate output
.nuxt
dist
# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and not Next.js
# public
# vuepress build output
.vuepress/dist
# vuepress v2.x temp and cache directory
.temp
# Docusaurus cache and generated files
.docusaurus
.build
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
# TernJS port file
.tern-port
# Stores VSCode versions used for testing VSCode extensions
.vscode-test
# Yarn v2
.yarn/cache
.yarn/unplugged
# Yarn Plug'n'Play
.pnp.*
.yarn/cache/
.yarn/unplugged/
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*
# Vite local server cache
.vite
# Vitest cache
.vitest
# Storybook build outputs
.out/
.storybook-out/
# Monorepo-specific
apps/*/node_modules/
apps/*/dist/
apps/*/build/
apps/*/.turbo/
apps/*/.next/
apps/*/.nuxt/
apps/*/.cache/
apps/*/.parcel-cache/
apps/*/.vuepress/dist
apps/*/.docusaurus
apps/*/.build
apps/*/.serverless
apps/*/.fusebox
apps/*/.dynamodb
apps/*/.tern-port
apps/*/.vscode-test
apps/*/.yarn
apps/*/.vite
apps/*/.vitest
apps/*/.out
apps/*/.storybook-out
packages/*/node_modules/
packages/*/dist/
packages/*/build/
packages/*/.turbo/
packages/*/.next/
packages/*/.nuxt/
packages/*/.cache/
packages/*/.parcel-cache/
packages/*/.vuepress/dist
packages/*/.docusaurus
packages/*/.build
packages/*/.serverless
packages/*/.fusebox
packages/*/.dynamodb
packages/*/.tern-port
packages/*/.vscode-test
packages/*/.yarn
packages/*/.vite
packages/*/.vitest
packages/*/.out
packages/*/.storybook-out
libs/*/node_modules/
libs/*/dist/
libs/*/build/
libs/*/.turbo/
libs/*/.next/
libs/*/.nuxt/
libs/*/.cache/
libs/*/.parcel-cache/
libs/*/.vuepress/dist
libs/*/.docusaurus
libs/*/.build
libs/*/.serverless
libs/*/.fusebox
libs/*/.dynamodb
libs/*/.tern-port
libs/*/.vscode-test
libs/*/.yarn
libs/*/.vite
libs/*/.vitest
libs/*/.out
libs/*/.storybook-out
# Ignore lockfiles
**/package-lock.json
**/yarn.lock
**/pnpm-lock.yaml
# IDEs and editors
.vscode/
.idea/
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
# MacOS
.DS_Store
# Windows
Thumbs.db
ehthumbs.db
Desktop.ini
$RECYCLE.BIN/
# Linux
*~
docker-compose.yml
.env
packages/common/prisma/migrations
volumes
# Ignore .idea files in the Expo monorepo
**/.idea/

1
.npmrc Normal file
View File

@ -0,0 +1 @@
node-linker=hoisted

View File

@ -1,12 +1,10 @@
# 基础镜像
FROM node:20-alpine as base
# 设置 npm 镜像源
RUN yarn config set registry https://registry.npmmirror.com
# 全局安装 pnpm 并设置其镜像源
RUN yarn global add pnpm && pnpm config set registry https://registry.npmmirror.com
# 更改 apk 镜像源为阿里云
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories && \
yarn config set registry https://registry.npmmirror.com && \
yarn global add pnpm && \
pnpm config set registry https://registry.npmmirror.com
# 设置工作目录
WORKDIR /app
@ -16,64 +14,71 @@ COPY pnpm-workspace.yaml ./
# 首先复制 package.json, package-lock.json 和 pnpm-lock.yaml 文件
COPY package*.json pnpm-lock.yaml* ./
COPY tsconfig.json .
# 利用 Docker 缓存机制,如果依赖没有改变则不会重新执行 pnpm install
COPY tsconfig.base.json .
FROM base As server-build
WORKDIR /app
COPY packages/common /app/packages/common
COPY apps/back-worker /app/apps/back-worker
RUN pnpm install --filter back-worker
RUN pnpm install --filter common
RUN pnpm --filter common build
RUN pnpm run build:server
COPY apps/server /app/apps/server
RUN pnpm install --filter common && \
pnpm install --filter server && \
pnpm --filter common generate && \
pnpm --filter common build:cjs && \
pnpm --filter server build
FROM base As server-prod-dep
WORKDIR /app
COPY packages/common /app/packages/common
COPY apps/back-worker /app/apps/back-worker
RUN pnpm install --filter back-worker --prod
RUN pnpm install --filter common --prod
COPY apps/server /app/apps/server
RUN pnpm install --filter common --prod && \
pnpm install --filter server --prod && \
# 清理包管理器缓存
pnpm store prune && rm -rf /root/.npm && rm -rf /root/.cache
FROM server-prod-dep as server
WORKDIR /app
ENV NODE_ENV production
COPY --from=server-build /app/packages/common/dist ./packages/common/dist
COPY --from=server-build /app/apps/back-worker/dist ./apps/back-worker/dist
COPY apps/back-worker/entrypoint.sh ./apps/back-worker/entrypoint.sh
COPY --from=server-build /app/apps/server/dist ./apps/server/dist
COPY apps/server/entrypoint.sh ./apps/server/entrypoint.sh
RUN chmod +x ./apps/server/entrypoint.sh
# RUN apk add --no-cache postgresql-client
RUN chmod +x ./apps/back-worker/entrypoint.sh
RUN apk add postgresql-client
EXPOSE 3010
EXPOSE 3000
ENTRYPOINT [ "/app/apps/back-worker/entrypoint.sh" ]
ENTRYPOINT [ "/app/apps/server/entrypoint.sh" ]
FROM base AS front-app-build
FROM base AS web-build
# 复制其余文件到工作目录
COPY . .
RUN pnpm install
RUN pnpm run build:web
RUN pnpm install && pnpm --filter web build
# 第二阶段,使用 nginx 提供服务
FROM nginx:stable-alpine as front-app
FROM nginx:stable-alpine as web
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories
# 设置工作目录
WORKDIR /usr/share/nginx/html
# 设置环境变量
ENV NODE_ENV production
# 将构建的文件从上一阶段复制到当前镜像中
COPY --from=front-app-build /app/apps/front-app/build .
COPY --from=web-build /app/apps/web/dist .
# 删除默认的nginx配置文件并添加自定义配置
RUN rm /etc/nginx/conf.d/default.conf
COPY apps/front-app/nginx.conf /etc/nginx/conf.d
COPY apps/web/nginx.conf /etc/nginx/conf.d
# 添加 entrypoint 脚本,并确保其可执行
COPY apps/front-app/entrypoint.sh /usr/bin/
COPY apps/web/entrypoint.sh /usr/bin/
RUN chmod +x /usr/bin/entrypoint.sh
# 安装 envsubst 以支持环境变量替换
RUN apk add envsubst
# RUN apk add --no-cache envsubst
# RUN echo "http://mirrors.aliyun.com/alpine/v3.12/main/" > /etc/apk/repositories && \
# echo "http://mirrors.aliyun.com/alpine/v3.12/community/" >> /etc/apk/repositories && \
# apk update && \
RUN apk add --no-cache gettext
# 暴露 80 端口
EXPOSE 80

9
apps/server/.env.example Normal file → Executable file
View File

@ -1,6 +1,13 @@
DATABASE_URL="postgresql://root:Letusdoit000@localhost:5432/defender_app?schema=public"
REDIS_HOST=localhost
REDIS_PORT=6379
REDIS_PASSWORD=Letusdoit000
TUS_URL=http://localhost:8080
JWT_SECRET=/yT9MnLm/r6NY7ee2Fby6ihCHZl+nFx4OQFKupivrhA=
APP_URL=http://localhost:5173
PUSH_URL=http://dns:9092
PUSH_APPID=123
PUSH_APPSECRET=123
DEADLINE_CRON="0 0 8 * * *"
SERVER_PORT=3000
ADMIN_PHONE_NUMBER=13258117304
NODE_ENV=development

0
apps/server/.eslintrc.js Normal file → Executable file
View File

0
apps/server/.prettierrc Normal file → Executable file
View File

14
apps/server/README.md Normal file → Executable file
View File

@ -29,33 +29,33 @@
## Installation
```bash
$ pnpm install
$ yarn install
```
## Running the app
```bash
# development
$ pnpm run start
$ yarn run start
# watch mode
$ pnpm run start:dev
$ yarn run start:dev
# production mode
$ pnpm run start:prod
$ yarn run start:prod
```
## Test
```bash
# unit tests
$ pnpm run test
$ yarn run test
# e2e tests
$ pnpm run test:e2e
$ yarn run test:e2e
# test coverage
$ pnpm run test:cov
$ yarn run test:cov
```
## Support

View File

@ -1,24 +1,41 @@
#!/bin/sh
# # 从 DATABASE_URL 环境变量中提取主机名、端口和用户名
# DB_HOST=$(echo $DATABASE_URL | cut -d '@' -f 2 | cut -d ':' -f 1)
# DB_PORT=$(echo $DATABASE_URL | cut -d ':' -f 4 | cut -d '/' -f 1)
# DB_USER=$(echo $DATABASE_URL | cut -d '/' -f 3 | cut -d ':' -f 1)
# # 检查数据库是否就绪
# until pg_isready -h $DB_HOST -p $DB_PORT -U $DB_USER; do
# echo "Database is unavailable - sleeping"
# sleep 1
# done
# echo "Database is up"
# # 检查标记文件是否存在,如果不存在,则执行 prisma deploy 并创建标记文件
# # if [ ! -f "/app/prisma-deployed" ]; then
# # pnpm prisma generate
# # pnpm prisma migrate deploy
# # touch /app/prisma-deployed
# # fi
# # 启动主应用
# exec node apps/server/dist/main
# 从 DATABASE_URL 环境变量中提取主机名、端口和用户名
DB_HOST=$(echo $DATABASE_URL | cut -d '@' -f 2 | cut -d ':' -f 1)
DB_PORT=$(echo $DATABASE_URL | cut -d ':' -f 4 | cut -d '/' -f 1)
DB_USER=$(echo $DATABASE_URL | cut -d '/' -f 3 | cut -d ':' -f 1)
# 检查数据库是否就绪
until pg_isready -h $DB_HOST -p $DB_PORT -U $DB_USER; do
until nc -z $DB_HOST $DB_PORT; do
echo "Database is unavailable - sleeping"
sleep 1
done
echo "Database is up"
# 检查标记文件是否存在,如果不存在,则执行 prisma deploy 并创建标记文件
# if [ ! -f "/app/prisma-deployed" ]; then
# pnpm prisma generate
# pnpm prisma migrate deploy
# touch /app/prisma-deployed
# fi
# 启动主应用
exec node apps/back-worker/dist/main
exec node apps/server/dist/main

0
apps/server/nest-cli.json Normal file → Executable file
View File

0
apps/server/package.json Normal file → Executable file
View File

View File

@ -1,6 +1,6 @@
import { Injectable } from '@nestjs/common';
import { TrpcService } from '@server/trpc/trpc.service';
import { ChangedRows, ObjectType, Prisma } from '@nicestack/common';
import { Prisma } from '@nicestack/common';
import { PostService } from './post.service';
import { z, ZodType } from 'zod';
const PostCreateArgsSchema: ZodType<Prisma.PostCreateArgs> = z.any();

View File

@ -1,5 +1,5 @@
import { db, Post, PostType, UserProfile, VisitType } from "@nicestack/common";
import { getTroubleWithRelation } from "../trouble/utils";
export async function setPostRelation(params: { data: Post, staff?: UserProfile }) {
const { data, staff } = params
const limitedComments = await db.post.findMany({
@ -32,13 +32,13 @@ export async function setPostRelation(params: { data: Post, staff?: UserProfile
visitType: VisitType.READED,
},
});
const trouble = await getTroubleWithRelation(data.referenceId, staff)
// const trouble = await getTroubleWithRelation(data.referenceId, staff)
Object.assign(data, {
readed,
readedCount,
limitedComments,
commentsCount,
trouble
// trouble
})
}

View File

@ -2,7 +2,6 @@ import { Injectable } from '@nestjs/common';
import { TransformService } from './transform.service';
import { TransformMethodSchema} from '@nicestack/common';
import { TrpcService } from '@server/trpc/trpc.service';
@Injectable()
export class TransformRouter {
constructor(

View File

@ -7,10 +7,13 @@ import {
db,
Prisma,
Staff,
GetTroubleLevel,
UserProfile,
TroubleDto,
ObjectType,
RiskState,
} from '@nicestack/common';
import dayjs from 'dayjs';
import * as argon2 from 'argon2';
import { TaxonomyService } from '@server/models/taxonomy/taxonomy.service';
import { uploadFile } from '@server/utils/tool';

View File

@ -14,5 +14,6 @@ export default async function (job: Job<any, any, CustomJobType>) {
logger.log(`push message ${job.data.id}`)
pushService.messagePush(job.data.registerToken, job.data.messageContent)
break
}
}

View File

@ -2,7 +2,7 @@ import { Injectable, OnModuleInit } from "@nestjs/common";
import { WebSocketType } from "../types";
import { BaseWebSocketServer } from "../base/base-websocket-server";
import EventBus, { CrudOperation } from "@server/utils/event-bus";
import { ObjectType, SocketMsgType, TroubleDto, MessageDto, PostDto, PostType } from "@nicestack/common";
import { ObjectType, SocketMsgType, MessageDto, PostDto, PostType } from "@nicestack/common";
@Injectable()
export class RealtimeServer extends BaseWebSocketServer implements OnModuleInit {
onModuleInit() {
@ -11,11 +11,7 @@ export class RealtimeServer extends BaseWebSocketServer implements OnModuleInit
const receiverIds = (data as Partial<MessageDto>).receivers.map(receiver => receiver.id)
this.sendToUsers(receiverIds, { type: SocketMsgType.NOTIFY, payload: { objectType: ObjectType.MESSAGE } })
}
if (type === ObjectType.TROUBLE) {
const trouble = data as Partial<TroubleDto>
this.sendToRoom('troubles', { type: SocketMsgType.NOTIFY, payload: { objectType: ObjectType.TROUBLE } })
this.sendToRoom(trouble.id, { type: SocketMsgType.NOTIFY, payload: { objectType: ObjectType.TROUBLE } })
}
if (type === ObjectType.POST) {
const post = data as Partial<PostDto>
if (post.type === PostType.TROUBLE_INSTRUCTION || post.type === PostType.TROUBLE_PROGRESS) {

View File

@ -22,6 +22,7 @@ import {
getCounts,
getRandomImageLinks
} from './utils';
import { StaffService } from '@server/models/staff/staff.service';
@Injectable()
export class GenDevService {
@ -40,6 +41,7 @@ export class GenDevService {
deptGeneratedCount = 0;
constructor(
private readonly appConfigService: AppConfigService,
private readonly departmentService: DepartmentService,
private readonly staffService: StaffService,
private readonly termService: TermService,
@ -183,7 +185,6 @@ export class GenDevService {
}
}
private async createDepartment(
name: string,
parentId?: string,

View File

@ -2,12 +2,14 @@ import { db, getRandomElement, getRandomIntInRange, getRandomTimeInterval, Objec
import dayjs from 'dayjs';
export interface DevDataCounts {
deptCount: number;
staffCount: number
termCount: number
}
export async function getCounts(): Promise<DevDataCounts> {
const counts = {
deptCount: await db.department.count(),
staffCount: await db.staff.count(),
termCount: await db.term.count(),
};

View File

@ -7,9 +7,8 @@
import { Injectable, Logger } from '@nestjs/common';
import dayjs from 'dayjs';
import { db, getUniqueItems, MessageMethodSchema, TroubleType, truncateString } from '@nicestack/common';
import { MessageService } from '@server/models/message/message.service';
import { extractUniqueStaffIds } from '@server/models/department/utils';
/**
*
@ -75,6 +74,8 @@ export class ReminderService {
*
*/
async remindDeadline() {
this.logger.log('开始检查截止日期以发送提醒。');
}
}

View File

@ -26,7 +26,6 @@ export class TrpcRouter {
private readonly taxonomy: TaxonomyRouter,
private readonly role: RoleRouter,
private readonly rolemap: RoleMapRouter,
private readonly transform: TransformRouter,
private readonly auth: AuthRouter,
private readonly app_config: AppConfigRouter,

0
apps/server/test/app.e2e-spec.ts Normal file → Executable file
View File

0
apps/server/test/jest-e2e.json Normal file → Executable file
View File

0
apps/server/tsconfig.build.json Normal file → Executable file
View File

44
auto.sh Normal file
View File

@ -0,0 +1,44 @@
#!/bin/bash
# 进入指定目录
cd /opt/projects/two-defender-app || exit
# 停止 server 容器
echo "停止 server 容器..."
sudo docker-compose stop server
# 移除 server 容器,自动确认
echo "移除 server 容器..."
yes | sudo docker-compose rm server
# 停止 web 容器
echo "停止 web 容器..."
sudo docker-compose stop web
# 移除 web 容器,自动确认
echo "移除 web 容器..."
yes | sudo docker-compose rm web
# 删除镜像
echo "删除 Docker 镜像..."
sudo docker image rm td-server:latest
sudo docker image rm td-web:latest
# 加载镜像
echo "加载 Docker 镜像..."
sudo docker load -i td-server.tar
sudo docker load -i td-web.tar
# 删除已加载的 tar 文件
sudo rm td-server.tar
sudo rm td-web.tar
# 启动服务
echo "启动服务..."
sudo docker-compose up -d
# 查看 server 容器的日志
echo "查看 server 容器的日志..."
sudo docker-compose logs server
echo "脚本执行完成。"

109
docker-compose.example.yml Normal file
View File

@ -0,0 +1,109 @@
version: "3.8"
services:
db:
image: postgres:latest
ports:
- "5432:5432"
environment:
- POSTGRES_DB=defender_app
- POSTGRES_USER=root
- POSTGRES_PASSWORD=Letusdoit000
volumes:
- ./volumes/postgres:/var/lib/postgresql/data
minio:
image: minio/minio
ports:
- "9000:9000"
- "9001:9001"
volumes:
- ./volumes/minio:/minio_data
environment:
- MINIO_ACCESS_KEY=minioadmin
- MINIO_SECRET_KEY=minioadmin
command: minio server /minio_data --console-address ":9001" -address ":9000"
healthcheck:
test:
[
"CMD",
"curl",
"-f",
"http://192.168.2.1:9001/minio/health/live"
]
interval: 30s
timeout: 20s
retries: 3
pgadmin:
image: dpage/pgadmin4
ports:
- "8082:80"
environment:
- PGADMIN_DEFAULT_EMAIL=insiinc@outlook.com
- PGADMIN_DEFAULT_PASSWORD=Letusdoit000
tusd:
image: tusproject/tusd
ports:
- "8080:8080"
environment:
- AWS_REGION=cn-north-1
- AWS_ACCESS_KEY_ID=minioadmin
- AWS_SECRET_ACCESS_KEY=minioadmin
command: -verbose -s3-bucket app -s3-endpoint http://minio:9000
volumes:
- ./volumes/tusd:/data
depends_on:
- minio
redis:
image: redis:latest
ports:
- "6379:6379"
volumes:
- ./config/redis.conf:/usr/local/etc/redis/redis.conf
- ./volumes/redis:/data
command: ["redis-server", "/usr/local/etc/redis/redis.conf"]
# restic:
# image: restic/restic:latest
# environment:
# - RESTIC_REPOSITORY=/backup
# - RESTIC_PASSWORD=Letusdoit000
# volumes:
# - ./volumes/postgres:/data
# - ./volumes/restic-cache:/root/.cache/restic
# - ./backup:/backup # 本地目录挂载到容器内的 /backup
# - ./config/backup.sh:/usr/local/bin/backup.sh # Mount your script inside the container
# entrypoint: /usr/local/bin/backup.sh
# depends_on:
# - db
# web:
# image: td-web:latest
# ports:
# - "80:80"
# environment:
# - VITE_APP_SERVER_IP=192.168.79.77
# - VITE_APP_VERSION=0.3.0
# - VITE_APP_APP_NAME=两道防线管理后台
# server:
# image: td-server:latest
# ports:
# - "3000:3000"
# - "3001:3001"
# environment:
# - DATABASE_URL=postgresql://root:Letusdoit000@db:5432/defender_app?schema=public
# - REDIS_HOST=redis
# - REDIS_PORT=6379
# - REDIS_PASSWORD=Letusdoit000
# - TUS_URL=http://192.168.2.1:8080
# - JWT_SECRET=/yT9MnLm/r6NY7ee2Fby6ihCHZl+nFx4OQFKupivrhA=
# - PUSH_URL=http://dns:9092
# - PUSH_APPID=123
# - PUSH_APPSECRET=123
# - MINIO_HOST=minio
# - ADMIN_PHONE_NUMBER=13258117304
# - DEADLINE_CRON=0 0 8 * * *
# depends_on:
# - db
# - redis
networks:
default:
name: defender-app

View File

@ -1,60 +0,0 @@
version: "3.8"
services:
db:
image: postgres:latest
ports:
- "5432:5432"
environment:
- POSTGRES_DB=app
- POSTGRES_USER=root
- POSTGRES_PASSWORD=Letusdoit000
volumes:
- ./volumes/postgres:/var/lib/postgresql/data
minio:
image: minio/minio
ports:
- "9000:9000"
- "9001:9001"
volumes:
- ./volumes/minio:/data
environment:
- MINIO_ACCESS_KEY=minioadmin
- MINIO_SECRET_KEY=minioadmin
command: server /data --console-address ":9001" -address ":9000"
healthcheck:
test:
[
"CMD",
"curl",
"-f",
"http://localhost:9001/minio/health/live"
]
interval: 30s
timeout: 20s
retries: 3
pgadmin:
image: dpage/pgadmin4
ports:
- "8081:80"
environment:
- PGADMIN_DEFAULT_EMAIL=insiinc@outlook.com
- PGADMIN_DEFAULT_PASSWORD=Letusdoit000
tusd:
image: tusproject/tusd
ports:
- "8080:8080"
environment:
- AWS_REGION=cn-north-1
- AWS_ACCESS_KEY_ID=minioadmin
- AWS_SECRET_ACCESS_KEY=minioadmin
command: -verbose -s3-bucket app -s3-endpoint http://minio:9000
volumes:
- ./volumes/tusd:/data
redis:
image: redis:latest
ports:
- "6379:6379"
volumes:
- ./volumes/redis:/data

View File

@ -19,6 +19,7 @@
"tinycolor2": "^1.6.0"
},
"peerDependencies": {
"dayjs": "^1.11.12",
"react": "18.2.0",
"@nicestack/common": "workspace:^",
"@tanstack/query-async-storage-persister": "^5.51.9",

View File

@ -5,7 +5,6 @@ export * from "./useTerm"
export * from "./useRole"
export * from "./useRoleMap"
export * from "./useTransform"
export * from "./useTrouble"
export * from "./useTaxonomy"
export * from "./useVisitor"
export * from "./useMessage"

View File

@ -0,0 +1,34 @@
import { getQueryKey } from "@trpc/react-query";
import { api } from "../trpc"; // Adjust path as necessary
import { useQueryClient } from "@tanstack/react-query";
export function useTransform() {
const queryClient = useQueryClient();
const queryKey = getQueryKey(api.transform);
const termQueryKey = getQueryKey(api.term);
const deptQueryKey = getQueryKey(api.department);
const importTerms = api.transform.importTerms.useMutation({
onSuccess: () => {
queryClient.invalidateQueries({ queryKey });
queryClient.invalidateQueries({ queryKey: termQueryKey });
},
});
const importDepts = api.transform.importDepts.useMutation({
onSuccess: () => {
queryClient.invalidateQueries({ queryKey });
queryClient.invalidateQueries({ queryKey: deptQueryKey });
},
});
const importStaffs = api.transform.importStaffs.useMutation({
onSuccess: () => {
queryClient.invalidateQueries({ queryKey });
},
});
return {
importTerms,
importDepts,
importStaffs,
};
}

View File

@ -8,7 +8,7 @@ export function useVisitor() {
const create = api.visitor.create.useMutation({
onSuccess() {
utils.visitor.invalidate();
utils.trouble.invalidate();
// utils.trouble.invalidate();
},
});
/**
@ -20,67 +20,67 @@ export function useVisitor() {
updateFn: (item: any, variables: any) => any
) => ({
// 在请求发送前执行本地数据预更新
onMutate: async (variables: any) => {
const previousDataList: any[] = [];
// 动态生成参数列表,包括星标和其他参数
// onMutate: async (variables: any) => {
// const previousDataList: any[] = [];
// // 动态生成参数列表,包括星标和其他参数
const paramsList = troubleParams.getItems();
console.log(paramsList.length);
// 遍历所有参数列表,执行乐观更新
for (const params of paramsList) {
// 取消可能的并发请求
await utils.trouble.findManyWithCursor.cancel();
// 获取并保存当前数据
const previousData =
utils.trouble.findManyWithCursor.getInfiniteData({
...params,
});
previousDataList.push(previousData);
// 执行乐观更新
utils.trouble.findManyWithCursor.setInfiniteData(
{
...params,
},
(oldData) => {
if (!oldData) return oldData;
return {
...oldData,
pages: oldData.pages.map((page) => ({
...page,
items: page.items.map((item) =>
item.id === variables?.troubleId
? updateFn(item, variables)
: item
),
})),
};
}
);
}
// const paramsList = troubleParams.getItems();
// console.log(paramsList.length);
// // 遍历所有参数列表,执行乐观更新
// for (const params of paramsList) {
// // 取消可能的并发请求
// await utils.trouble.findManyWithCursor.cancel();
// // 获取并保存当前数据
// const previousData =
// utils.trouble.findManyWithCursor.getInfiniteData({
// ...params,
// });
// previousDataList.push(previousData);
// // 执行乐观更新
// utils.trouble.findManyWithCursor.setInfiniteData(
// {
// ...params,
// },
// (oldData) => {
// if (!oldData) return oldData;
// return {
// ...oldData,
// pages: oldData.pages.map((page) => ({
// ...page,
// items: page.items.map((item) =>
// item.id === variables?.troubleId
// ? updateFn(item, variables)
// : item
// ),
// })),
// };
// }
// );
// }
return { previousDataList };
},
// 错误处理:数据回滚
onError: (_err: any, _variables: any, context: any) => {
const paramsList = troubleParams.getItems();
paramsList.forEach((params, index) => {
if (context?.previousDataList?.[index]) {
utils.trouble.findManyWithCursor.setInfiniteData(
{ ...params },
context.previousDataList[index]
);
}
});
},
// 成功后的缓存失效
onSuccess: (_: any, variables: any) => {
utils.visitor.invalidate();
utils.trouble.findFirst.invalidate({
where: {
id: (variables as any)?.troubleId,
},
});
},
// return { previousDataList };
// },
// // 错误处理:数据回滚
// onError: (_err: any, _variables: any, context: any) => {
// const paramsList = troubleParams.getItems();
// paramsList.forEach((params, index) => {
// if (context?.previousDataList?.[index]) {
// utils.trouble.findManyWithCursor.setInfiniteData(
// { ...params },
// context.previousDataList[index]
// );
// }
// });
// },
// // 成功后的缓存失效
// onSuccess: (_: any, variables: any) => {
// utils.visitor.invalidate();
// utils.trouble.findFirst.invalidate({
// where: {
// id: (variables as any)?.troubleId,
// },
// });
// },
});
// 定义具体的mutation
const read = api.visitor.create.useMutation(

View File

@ -4,7 +4,7 @@
*/
import mitt from 'mitt';
import { DepartmentDto, ObjectType, RoleMapDto, StaffDto, TermDto, TroubleDto } from '@nicestack/common';
import { DepartmentDto, ObjectType, RoleMapDto, StaffDto, TermDto, } from '@nicestack/common';
/**
* CRUD操作
@ -38,7 +38,6 @@ type EmitChangeFunction<T> = (data: Partial<T>, operation: CrudOperation) => voi
*/
interface EmitChangeHandlers {
[ObjectType.STAFF]: EmitChangeFunction<StaffDto>; // 员工数据变更处理器
[ObjectType.TROUBLE]: EmitChangeFunction<TroubleDto>; // 问题数据变更处理器
[ObjectType.ROLE_MAP]: EmitChangeFunction<RoleMapDto>; // 角色映射数据变更处理器
[ObjectType.DEPARTMENT]: EmitChangeFunction<DepartmentDto>; // 部门数据变更处理器
[ObjectType.TERM]: EmitChangeFunction<TermDto> // 术语数据变更处理器
@ -66,21 +65,6 @@ const emitChangeHandlers: EmitChangeHandlers = {
});
},
[ObjectType.TROUBLE]: (data, operation) => {
// 转换问题数据,包含额外字段
const rowData = {
...data,
dept_name: data.department?.name,
possible_result: data.possibleResult,
};
// 发出问题数据变更事件
EventBus.emit("dataChanged", {
type: ObjectType.TROUBLE,
operation,
data: [rowData]
});
},
[ObjectType.ROLE_MAP]: (data, operation) => {
// 转换角色映射数据,包含额外字段
const rowData = {

View File

@ -9,7 +9,6 @@ import {
TroubleType,
VisitType,
} from "./enum";
import { troubleUnDetailSelect } from "./select";
export const InitRoles: {
name: string;

View File

@ -1,5 +1,3 @@
import { troubleUnDetailSelect } from "./select";
export enum SocketMsgType {
NOTIFY,
}

View File

@ -4,7 +4,6 @@ import type {
Term,
Message,
Post,
Trouble,
RoleMap,
} from "@prisma/client";
import { SocketMsgType, RolePerms } from "./enum";

View File

@ -3,6 +3,7 @@
"target": "es2022",
"module": "esnext",
"lib": [
"DOM",
"es2022"
],
"declaration": true,

12176
pnpm-lock.yaml Normal file

File diff suppressed because it is too large Load Diff

View File

@ -2,6 +2,5 @@ packages:
# all packages in direct subdirs of packages/
- 'packages/*'
- 'apps/*'
- 'libs/*'
# exclude packages that are inside test directories
# - '!**/test/**'

7
tsconfig.base.json Normal file → Executable file
View File

@ -3,9 +3,8 @@
"baseUrl": ".",
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"incremental": true,
"skipLibCheck": true,
"strictNullChecks": true,
"strictNullChecks": false,
"strictBindCallApply": true,
"forceConsistentCasingInFileNames": true,
"noFallthroughCasesInSwitch": true,
@ -17,10 +16,10 @@
"strictPropertyInitialization": false,
"paths": {
"@server/*": [
"./apps/server/src/*"
"./apps/server/src/*",
],
"@web/*": [
"apps/web/*"
"./apps/web/*",
],
}
}