refactor: 重构项目结构和代码组织

This commit is contained in:
2026-04-23 23:40:28 +08:00
parent 481555c257
commit ab860c61d9
165 changed files with 51 additions and 17248 deletions

View File

@@ -1,51 +0,0 @@
# Dependencies
node_modules/
# Build output
dist/
build/
# Environment files
.env
.env.local
.env.*.local
# IDE
.vscode/
.idea/
*.swp
*.swo
# OS
.DS_Store
Thumbs.db
# Logs
logs/
*.log
npm-debug.log*
# Data
data/
# Git
.git/
.gitignore
# Docker
Dockerfile
docker-compose*.yml
.dockerignore
# Documentation
*.md
docs/
# Tests
test/
tests/
coverage/
# Misc
*.tmp
*.temp

61
.gitignore vendored
View File

@@ -1,61 +0,0 @@
# Dependencies
node_modules/
.pnp
.pnp.js
# Testing
coverage/
*.lcov
# Production builds
dist/
build/
server/dist/
client/dist/
# Environment variables
.env
.env.local
.env.*.local
!.env.example
# IDE
.vscode/
.idea/
*.swp
*.swo
*~
# OS
.DS_Store
Thumbs.db
# Logs
logs/
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
# Data persistence
data/
*.db
*.sqlite
# Temporary files
tmp/
temp/
*.tmp
*.temp
# Docker
docker-compose.override.yml
# Secrets
*.pem
*.key
secrets/
# Shared types build output
shared/dist/

5
.idea/.gitignore generated vendored Normal file
View File

@@ -0,0 +1,5 @@
# 默认忽略的文件
/shelf/
/workspace.xml
# 基于编辑器的 HTTP 客户端请求
/httpRequests/

6
.idea/encodings.xml generated Normal file
View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding">
<file url="file://$PROJECT_DIR$/make.bat" charset="GB2312" />
</component>
</project>

View File

@@ -0,0 +1,12 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="PyUnresolvedReferencesInspection" enabled="true" level="WARNING" enabled_by_default="true">
<option name="ignoredIdentifiers">
<list>
<option value="requests.models.Response.__getitem__" />
</list>
</option>
</inspection_tool>
</profile>
</component>

View File

@@ -0,0 +1,6 @@
<component name="InspectionProjectProfileManager">
<settings>
<option name="USE_PROJECT_PROFILE" value="false" />
<version value="1.0" />
</settings>
</component>

8
.idea/modules.xml generated Normal file
View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/sillytavern-repalice.iml" filepath="$PROJECT_DIR$/.idea/sillytavern-repalice.iml" />
</modules>
</component>
</project>

8
.idea/sillytavern-repalice.iml generated Normal file
View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="PYTHON_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

6
.idea/vcs.xml generated Normal file
View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

View File

@@ -1,254 +0,0 @@
# 项目架构检查报告
## ✅ 架构合理性评估
### **1. 整体结构评分9/10** ⭐⭐⭐⭐⭐
---
## 📁 目录结构分析
### **✅ 优点**
#### **1. 清晰的三层架构**
```
project/
├── server/ # 后端 (NestJS)
├── client/ # 前端 (Vue3)
├── shared/ # 共享类型系统
├── data/ # 持久化数据
├── docker/ # Docker 配置
└── docs/ # 文档
```
- ✅ 前后端分离明确
- ✅ 共享类型独立管理
- ✅ 数据和配置隔离
#### **2. 后端模块化设计 (DDD)**
```
server/src/
├── core/ # 核心基础设施层
├── modules/ # 业务模块层
├── shared/ # 共享工具层
├── events/ # 事件系统
├── queues/ # 任务队列
└── common/ # 通用基类
```
- ✅ 符合领域驱动设计
- ✅ 职责分离清晰
- ✅ 易于扩展和维护
#### **3. 模块内部结构标准**
```
modules/{module}/
├── controllers/ # API 控制器
├── services/ # 业务逻辑
├── adapters/ # 格式转换适配器
├── dto/ # 数据传输对象
└── interfaces/ # 接口定义
```
- ✅ 遵循 MVC 模式
- ✅ 适配器模式实现良好
- ✅ 依赖注入友好
#### **4. 前端分层合理**
```
client/src/
├── api/ # API 客户端层
├── components/ # UI 组件层
├── views/ # 页面视图层
├── composables/ # 组合式函数
├── stores/ # 状态管理
├── router/ # 路由配置
├── types/ # 类型定义
└── utils/ # 工具函数
```
- ✅ 符合 Vue3 最佳实践
- ✅ 关注点分离
- ✅ 可复用性强
#### **5. 共享类型系统设计**
```
shared/types/
├── sillytavern/ # ST 原生格式(兼容层)
├── extended/ # 扩展格式(增强层)
├── adapters/ # 适配器接口
└── index.ts # 统一导出
```
- ✅ 双层格式设计优秀
- ✅ 完全兼容 SillyTavern
- ✅ 扩展性强
---
## ⚠️ 需要改进的地方
### **1. 缺少配置文件**
```
❌ server/.env.example
❌ client/.env.example
❌ server/tsconfig.build.json (已有但需检查)
```
**建议:**
- 创建 `.env.example` 模板文件
- 确保所有必要的环境变量都有文档
### **2. 模块完整性不一致**
**完整的模块:**
-`chat/` - 有 adapters
-`world-info/` - 有 adapters
-`preset/` - 有 adapters
**不完整的模块:**
- ⚠️ `auth/` - 缺少 adapters
- ⚠️ `llm/` - 缺少 adapters
- ⚠️ `workflow/` - 缺少 adapters
- ⚠️ `data-store/` - 缺少 adapters
- ⚠️ `file-management/` - 缺少 adapters
- ⚠️ `import-export/` - 缺少 adapters
**建议:**
为每个模块添加标准的子目录结构,即使暂时为空。
### **3. 前端目录有空文件夹**
```
client/src/
├── composables/ # 空
├── plugins/ # 空
├── services/ # 空
└── store/ # 空(应该是 stores/
```
**建议:**
- 删除空的 `store/` 目录(已有 `stores/`
-`composables/` 中添加基础 composable
-`plugins/` 中添加必要的插件
### **4. 缺少测试目录结构**
```
tests/ # 空
```
**建议:**
```
tests/
├── unit/ # 单元测试
├── integration/ # 集成测试
└── e2e/ # 端到端测试
```
### **5. Docker 配置可以优化**
**当前配置:**
- ✅ 开发环境热重载配置正确
- ✅ 端口映射合理23337, 23338
- ✅ 卷挂载正确
**建议改进:**
- 添加 `.dockerignore` 文件
- 考虑添加 Nginx 反向代理(生产环境)
- 添加日志卷挂载
---
## 🎯 架构优势总结
### **1. 数据类型设计优秀** ⭐⭐⭐⭐⭐
- 双层格式ST + Extended
- 适配器模式实现完善
- 完全向后兼容
### **2. 模块化程度高** ⭐⭐⭐⭐⭐
- 每个模块独立
- 低耦合高内聚
- 易于团队协作
### **3. 技术栈选择合理** ⭐⭐⭐⭐⭐
- NestJS + Vue3 = 现代全栈
- TypeScript 全程类型安全
- Docker 容器化部署
### **4. 可扩展性强** ⭐⭐⭐⭐⭐
- 事件系统预留
- 任务队列预留
- 微服务演进可能
### **5. 开发体验好** ⭐⭐⭐⭐
- 热重载支持
- 路径别名配置
- 类型提示完整
---
## 📋 建议的改进清单
### **高优先级**
1. ✅ 创建 `.env.example` 文件
2. ✅ 统一模块目录结构
3. ✅ 清理空文件夹
4. ✅ 添加 `.dockerignore`
### **中优先级**
5. ⏳ 创建基础 Composables
6. ⏳ 添加单元测试框架
7. ⏳ 完善 Docker 配置
8. ⏳ 添加 CI/CD 配置
### **低优先级**
9. 📝 编写 API 文档
10. 📝 添加部署指南
11. 📝 创建贡献指南
---
## 🚀 Docker 配置说明
### **端口映射**
- 后端:`23337:3000` (宿主机 23337 → 容器 3000
- 前端:`23338:5173` (宿主机 23338 → 容器 5173
### **热重载支持**
```yaml
volumes:
- ./server:/usr/src/app # 代码挂载
- ./shared:/usr/src/shared # 共享类型挂载
- /usr/src/app/node_modules # 排除 node_modules
```
### **数据持久化**
```yaml
volumes:
- ./data:/usr/src/app/data # 数据库和文件
- ./.env:/usr/src/app/.env:ro # 环境变量(只读)
```
### **健康检查**
```yaml
healthcheck:
test: ["CMD", "wget", "--spider", "-q", "http://localhost:3000/api"]
interval: 30s
timeout: 10s
retries: 3
```
---
## ✅ 总体评价
**架构评分9/10**
**优点:**
- 设计思路清晰
- 技术选型现代
- 可扩展性强
- 兼容性好
**待改进:**
- 部分模块不完整
- 缺少一些配置文件
- 测试框架未搭建
**结论:**
整体架构非常合理,具备良好的可扩展性和维护性。只需补充一些细节即可投入开发。

View File

@@ -1,6 +0,0 @@
node_modules
dist
.env
.git
.idea
.vscode

View File

@@ -1,37 +0,0 @@
# =====================================================
# API Configuration
# =====================================================
VITE_API_URL=http://localhost:23337/api
VITE_WS_URL=ws://localhost:23337
# =====================================================
# Application Configuration
# =====================================================
VITE_APP_NAME=SillyTavern Replace
VITE_APP_VERSION=1.0.0
# =====================================================
# Feature Flags
# =====================================================
VITE_ENABLE_WEBSOCKET=true
VITE_ENABLE_SSE=true
# =====================================================
# LLM Configuration (Public - Safe to Expose)
# =====================================================
VITE_DEFAULT_LLM_PROVIDER=openai
VITE_MAX_TOKENS=4096
VITE_DEFAULT_TEMPERATURE=0.7
# =====================================================
# UI Configuration
# =====================================================
VITE_THEME=light
VITE_LANGUAGE=zh-CN
VITE_ITEMS_PER_PAGE=20
# =====================================================
# Upload Configuration
# =====================================================
VITE_MAX_UPLOAD_SIZE=10485760
VITE_ALLOWED_FILE_TYPES=.json,.csv,.txt,.md,.png,.jpg

View File

@@ -1,17 +0,0 @@
# Development Dockerfile for Frontend (Vue3 + Vite)
FROM node:20-alpine
# Set working directory
WORKDIR /usr/src/app
# Copy package files
COPY package*.json ./
# Install dependencies
RUN npm install
# Expose Vite dev server port
EXPOSE 5173
# Default command (will be overridden by docker-compose)
CMD ["npm", "run", "dev", "--", "--host", "0.0.0.0", "--port", "5173"]

View File

@@ -1,42 +0,0 @@
# client
This template should help get you started developing with Vue 3 in Vite.
## Recommended IDE Setup
[VS Code](https://code.visualstudio.com/) + [Vue (Official)](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur).
## Recommended Browser Setup
- Chromium-based browsers (Chrome, Edge, Brave, etc.):
- [Vue.js devtools](https://chromewebstore.google.com/detail/vuejs-devtools/nhdogjmejiglipccpnnnanhbledajbpd)
- [Turn on Custom Object Formatter in Chrome DevTools](http://bit.ly/object-formatters)
- Firefox:
- [Vue.js devtools](https://addons.mozilla.org/en-US/firefox/addon/vue-js-devtools/)
- [Turn on Custom Object Formatter in Firefox DevTools](https://fxdx.dev/firefox-devtools-custom-object-formatters/)
## Type Support for `.vue` Imports in TS
TypeScript cannot handle type information for `.vue` imports by default, so we replace the `tsc` CLI with `vue-tsc` for type checking. In editors, we need [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) to make the TypeScript language service aware of `.vue` types.
## Customize configuration
See [Vite Configuration Reference](https://vite.dev/config/).
## Project Setup
```sh
npm install
```
### Compile and Hot-Reload for Development
```sh
npm run dev
```
### Type-Check, Compile and Minify for Production
```sh
npm run build
```

1
client/env.d.ts vendored
View File

@@ -1 +0,0 @@
/// <reference types="vite/client" />

View File

@@ -1,13 +0,0 @@
<!DOCTYPE html>
<html lang="">
<head>
<meta charset="UTF-8">
<link rel="icon" href="/favicon.ico">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Vite App</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>

3148
client/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,33 +0,0 @@
{
"name": "client",
"version": "0.0.0",
"private": true,
"type": "module",
"scripts": {
"dev": "vite",
"build": "run-p type-check \"build-only {@}\" --",
"preview": "vite preview",
"build-only": "vite build",
"type-check": "vue-tsc --build"
},
"dependencies": {
"pinia": "^3.0.4",
"vue": "^3.5.32",
"vue-router": "^5.0.4"
},
"devDependencies": {
"@tsconfig/node24": "^24.0.4",
"@types/node": "^24.12.2",
"@vitejs/plugin-vue": "^6.0.6",
"@vitejs/plugin-vue-jsx": "^5.1.5",
"@vue/tsconfig": "^0.9.1",
"npm-run-all2": "^8.0.4",
"typescript": "~6.0.0",
"vite": "^8.0.8",
"vite-plugin-vue-devtools": "^8.1.1",
"vue-tsc": "^3.2.6"
},
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

View File

@@ -1,41 +0,0 @@
<template>
<div class="app-layout">
<!-- 顶部工具栏 -->
<TopToolbar />
<!-- 主体内容区 -->
<div class="main-content">
<!-- 左侧面板 -->
<LeftPanel />
<!-- 中间聊天区 -->
<CenterChat />
<!-- 右侧工具面板 -->
<RightPanel />
</div>
</div>
</template>
<script setup lang="ts">
import TopToolbar from './components/layout/TopToolbar.vue';
import LeftPanel from './components/layout/LeftPanel.vue';
import CenterChat from './components/layout/CenterChat.vue';
import RightPanel from './components/layout/RightPanel.vue';
</script>
<style scoped>
.app-layout {
display: flex;
flex-direction: column;
height: 100vh;
width: 100vw;
overflow: hidden;
}
.main-content {
display: flex;
flex: 1;
overflow: hidden;
}
</style>

0
client/src/api/index.ts Normal file
View File

View File

@@ -1,86 +0,0 @@
/* color palette from <https://github.com/vuejs/theme> */
:root {
--vt-c-white: #ffffff;
--vt-c-white-soft: #f8f8f8;
--vt-c-white-mute: #f2f2f2;
--vt-c-black: #181818;
--vt-c-black-soft: #222222;
--vt-c-black-mute: #282828;
--vt-c-indigo: #2c3e50;
--vt-c-divider-light-1: rgba(60, 60, 60, 0.29);
--vt-c-divider-light-2: rgba(60, 60, 60, 0.12);
--vt-c-divider-dark-1: rgba(84, 84, 84, 0.65);
--vt-c-divider-dark-2: rgba(84, 84, 84, 0.48);
--vt-c-text-light-1: var(--vt-c-indigo);
--vt-c-text-light-2: rgba(60, 60, 60, 0.66);
--vt-c-text-dark-1: var(--vt-c-white);
--vt-c-text-dark-2: rgba(235, 235, 235, 0.64);
}
/* semantic color variables for this project */
:root {
--color-background: var(--vt-c-white);
--color-background-soft: var(--vt-c-white-soft);
--color-background-mute: var(--vt-c-white-mute);
--color-border: var(--vt-c-divider-light-2);
--color-border-hover: var(--vt-c-divider-light-1);
--color-heading: var(--vt-c-text-light-1);
--color-text: var(--vt-c-text-light-1);
--section-gap: 160px;
}
@media (prefers-color-scheme: dark) {
:root {
--color-background: var(--vt-c-black);
--color-background-soft: var(--vt-c-black-soft);
--color-background-mute: var(--vt-c-black-mute);
--color-border: var(--vt-c-divider-dark-2);
--color-border-hover: var(--vt-c-divider-dark-1);
--color-heading: var(--vt-c-text-dark-1);
--color-text: var(--vt-c-text-dark-2);
}
}
*,
*::before,
*::after {
box-sizing: border-box;
margin: 0;
font-weight: normal;
}
body {
min-height: 100vh;
color: var(--color-text);
background: var(--color-background);
transition:
color 0.5s,
background-color 0.5s;
line-height: 1.6;
font-family:
Inter,
-apple-system,
BlinkMacSystemFont,
'Segoe UI',
Roboto,
Oxygen,
Ubuntu,
Cantarell,
'Fira Sans',
'Droid Sans',
'Helvetica Neue',
sans-serif;
font-size: 15px;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}

View File

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 261.76 226.69"><path d="M161.096.001l-30.225 52.351L100.647.001H-.005l130.877 226.688L261.749.001z" fill="#41b883"/><path d="M161.096.001l-30.225 52.351L100.647.001H52.346l78.526 136.01L209.398.001z" fill="#34495e"/></svg>

Before

Width:  |  Height:  |  Size: 276 B

View File

@@ -1,35 +0,0 @@
@import './base.css';
#app {
max-width: 1280px;
margin: 0 auto;
padding: 2rem;
font-weight: normal;
}
a,
.green {
text-decoration: none;
color: hsla(160, 100%, 37%, 1);
transition: 0.4s;
padding: 3px;
}
@media (hover: hover) {
a:hover {
background-color: hsla(160, 100%, 37%, 0.2);
}
}
@media (min-width: 1024px) {
body {
display: flex;
place-items: center;
}
#app {
display: grid;
grid-template-columns: 1fr 1fr;
padding: 0 2rem;
}
}

View File

View File

View File

@@ -1,72 +0,0 @@
<template>
<div :class="['message-item', message.senderRole]">
<div class="message-header">
<span class="sender-name">{{ message.senderName }}</span>
<span class="timestamp">{{ formatTime(message.timestamp) }}</span>
</div>
<div class="message-content">{{ message.mes }}</div>
</div>
</template>
<script setup lang="ts">
interface Message {
id: string;
senderName: string;
senderRole: 'user' | 'assistant' | 'system';
mes: string;
timestamp: Date;
}
defineProps<{
message: Message;
}>();
function formatTime(date: Date) {
return new Date(date).toLocaleTimeString('zh-CN', {
hour: '2-digit',
minute: '2-digit'
});
}
</script>
<style scoped>
.message-item {
padding: 12px;
border-radius: 8px;
margin-bottom: 8px;
background: #ffffff;
border: 1px solid #e0e0e0;
}
.message-item.user {
background: #e3f2fd;
border-color: #bbdefb;
margin-left: 20%;
}
.message-item.assistant {
background: #f1f8e9;
border-color: #dcedc8;
margin-right: 20%;
}
.message-header {
display: flex;
justify-content: space-between;
margin-bottom: 8px;
font-size: 12px;
color: #7f8c8d;
}
.sender-name {
font-weight: 600;
color: #2c3e50;
}
.message-content {
line-height: 1.6;
white-space: pre-wrap;
font-size: 14px;
color: #2c3e50;
}
</style>

View File

@@ -1,21 +0,0 @@
<template>
<div class="api-config-view">
<h3>🔌 API 配置</h3>
<p>API 配置管理界面</p>
</div>
</template>
<style scoped>
.api-config-view {
padding: 8px;
}
h3 {
font-size: 14px;
color: #2c3e50;
margin: 0 0 12px 0;
}
p {
font-size: 13px;
color: #7f8c8d;
}
</style>

View File

@@ -1,21 +0,0 @@
<template>
<div class="gallery-view">
<h3>🖼 画廊</h3>
<p>LLM 生成的图片将显示在这里</p>
</div>
</template>
<style scoped>
.gallery-view {
padding: 8px;
}
h3 {
font-size: 14px;
color: #2c3e50;
margin: 0 0 12px 0;
}
p {
font-size: 13px;
color: #7f8c8d;
}
</style>

View File

@@ -1,21 +0,0 @@
<template>
<div class="preset-view">
<h3> 预设管理</h3>
<p>预设配置和条目展示</p>
</div>
</template>
<style scoped>
.preset-view {
padding: 8px;
}
h3 {
font-size: 14px;
color: #2c3e50;
margin: 0 0 12px 0;
}
p {
font-size: 13px;
color: #7f8c8d;
}
</style>

View File

@@ -1,21 +0,0 @@
<template>
<div class="world-book-view">
<h3>📚 世界书管理</h3>
<p>全局世界书激活和条目管理</p>
</div>
</template>
<style scoped>
.world-book-view {
padding: 8px;
}
h3 {
font-size: 14px;
color: #2c3e50;
margin: 0 0 12px 0;
}
p {
font-size: 13px;
color: #7f8c8d;
}
</style>

View File

@@ -1,21 +0,0 @@
<template>
<div class="dice-view">
<h3>🎲 骰子工具</h3>
<p>纯前端骰子功能</p>
</div>
</template>
<style scoped>
.dice-view {
padding: 8px;
}
h3 {
font-size: 14px;
color: #2c3e50;
margin: 0 0 12px 0;
}
p {
font-size: 13px;
color: #7f8c8d;
}
</style>

View File

@@ -1,21 +0,0 @@
<template>
<div class="rag-view">
<h3>🔍 RAG 检索</h3>
<p>RAG 知识检索功能</p>
</div>
</template>
<style scoped>
.rag-view {
padding: 8px;
}
h3 {
font-size: 14px;
color: #2c3e50;
margin: 0 0 12px 0;
}
p {
font-size: 13px;
color: #7f8c8d;
}
</style>

View File

@@ -1,21 +0,0 @@
<template>
<div class="world-book-call-view">
<h3>📚 世界书调用</h3>
<p>世界书条目调用记录</p>
</div>
</template>
<style scoped>
.world-book-call-view {
padding: 8px;
}
h3 {
font-size: 14px;
color: #2c3e50;
margin: 0 0 12px 0;
}
p {
font-size: 13px;
color: #7f8c8d;
}
</style>

View File

@@ -1,175 +0,0 @@
<template>
<main class="center-chat">
<!-- 消息列表 -->
<div class="message-list">
<CenterMessageItem
v-for="msg in messages"
:key="msg.id"
:message="msg"
/>
</div>
<!-- 输入区域 -->
<div class="input-area">
<!-- 选项框 -->
<div class="input-options">
<select v-model="sendOptions.role" class="option-select">
<option value="user">用户</option>
<option value="assistant">AI</option>
</select>
</div>
<!-- 输入框 -->
<textarea
v-model="inputMessage"
class="message-input"
placeholder="输入消息..."
@keydown.enter.ctrl="sendMessage"
></textarea>
<!-- 发送/停止按钮 -->
<button
:class="['send-btn', { sending: isSending }]"
@click="isSending ? stopGeneration() : sendMessage()"
>
{{ isSending ? '⏹ 停止' : '📤 发送' }}
</button>
</div>
</main>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import CenterMessageItem from '../features/center/MessageItem.vue';
interface Message {
id: string;
senderName: string;
senderRole: 'user' | 'assistant' | 'system';
mes: string;
timestamp: Date;
}
const messages = ref<Message[]>([
{
id: '1',
senderName: 'Assistant',
senderRole: 'assistant',
mes: '你好!有什么可以帮助你的吗?',
timestamp: new Date(),
},
]);
const inputMessage = ref('');
const isSending = ref(false);
const sendOptions = ref({
role: 'user' as 'user' | 'assistant',
});
function sendMessage() {
if (!inputMessage.value.trim()) return;
const newMessage: Message = {
id: Date.now().toString(),
senderName: sendOptions.value.role === 'user' ? 'User' : 'Assistant',
senderRole: sendOptions.value.role,
mes: inputMessage.value,
timestamp: new Date(),
};
messages.value.push(newMessage);
inputMessage.value = '';
isSending.value = true;
// 模拟 AI 回复
setTimeout(() => {
isSending.value = false;
}, 2000);
}
function stopGeneration() {
isSending.value = false;
}
</script>
<style scoped>
.center-chat {
display: flex;
flex-direction: column;
flex: 1;
background: #ffffff;
overflow: hidden;
}
.message-list {
flex: 1;
overflow-y: auto;
padding: 16px;
display: flex;
flex-direction: column;
gap: 12px;
}
.input-area {
display: flex;
align-items: flex-end;
gap: 8px;
padding: 16px;
background: #f8f9fa;
border-top: 2px solid #dee2e6;
flex-shrink: 0;
}
.input-options {
flex-shrink: 0;
}
.option-select {
padding: 8px;
border: 1px solid #ced4da;
border-radius: 4px;
background: white;
font-size: 13px;
}
.message-input {
flex: 1;
padding: 12px;
border: 1px solid #ced4da;
border-radius: 4px;
resize: none;
font-size: 14px;
min-height: 60px;
max-height: 200px;
font-family: inherit;
}
.message-input:focus {
outline: none;
border-color: #3498db;
}
.send-btn {
padding: 12px 20px;
border: none;
border-radius: 4px;
background: #3498db;
color: white;
cursor: pointer;
font-size: 14px;
font-weight: 500;
transition: background 0.2s;
flex-shrink: 0;
}
.send-btn:hover {
background: #2980b9;
}
.send-btn.sending {
background: #e74c3c;
}
.send-btn.sending:hover {
background: #c0392b;
}
</style>

View File

@@ -1,99 +0,0 @@
<template>
<aside class="left-panel">
<!-- 左侧-顶部分页标签 -->
<div class="panel-tabs">
<button
v-for="tab in tabs"
:key="tab.id"
:class="['tab-btn', { active: currentTab === tab.id }]"
@click="currentTab = tab.id"
>
{{ tab.icon }} {{ tab.label }}
</button>
</div>
<!-- 左侧-内容区域 -->
<div class="panel-content">
<LeftGalleryView v-if="currentTab === 'gallery'" />
<LeftApiConfigView v-if="currentTab === 'api'" />
<LeftPresetView v-if="currentTab === 'preset'" />
<LeftWorldBookView v-if="currentTab === 'worldbook'" />
</div>
</aside>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import LeftGalleryView from '../features/left/GalleryView.vue';
import LeftApiConfigView from '../features/left/ApiConfigView.vue';
import LeftPresetView from '../features/left/PresetView.vue';
import LeftWorldBookView from '../features/left/WorldBookView.vue';
const currentTab = ref('gallery');
const tabs = [
{ id: 'gallery', label: '画廊', icon: '🖼️' },
{ id: 'api', label: 'API', icon: '🔌' },
{ id: 'preset', label: '预设', icon: '⚙️' },
{ id: 'worldbook', label: '世界书', icon: '📚' },
];
</script>
<style scoped>
.left-panel {
display: flex;
flex-direction: column;
width: 25%;
min-width: 300px;
max-width: 400px;
background: #ffffff;
border-right: 1px solid #e0e0e0;
flex-shrink: 0;
}
.panel-tabs {
display: flex;
background: #f8f9fa;
border-bottom: 1px solid #e0e0e0;
padding: 6px 8px;
gap: 4px;
flex-shrink: 0;
}
.tab-btn {
flex: 1;
padding: 8px 6px;
border: 1px solid transparent;
border-radius: 4px;
background: transparent;
color: #7f8c8d;
cursor: pointer;
font-size: 12px;
font-weight: 500;
transition: all 0.2s;
display: flex;
flex-direction: column;
align-items: center;
gap: 2px;
}
.tab-btn:hover {
background: #ffffff;
border-color: #e0e0e0;
color: #2c3e50;
}
.tab-btn.active {
background: #ffffff;
border-color: #3498db;
color: #3498db;
box-shadow: 0 1px 3px rgba(52, 152, 219, 0.1);
}
.panel-content {
flex: 1;
overflow-y: auto;
padding: 16px;
background: #fafafa;
}
</style>

View File

@@ -1,267 +0,0 @@
<template>
<aside class="right-panel">
<!-- 右侧-顶部分页标签 -->
<div class="panel-tabs">
<button
v-for="tool in availableToolsList"
:key="tool.id"
:class="['tab-btn', { active: isToolActive(tool.id) }]"
@click="selectTool(tool.id)"
>
{{ tool.icon }} {{ tool.shortLabel }}
</button>
</div>
<!-- 右侧-上部分区最新使用的工具 -->
<div class="panel-section top-section">
<div class="section-header">
<span class="section-title">{{ recentTools[0]?.label || '未选择工具' }}</span>
<button class="collapse-btn" @click="toggleTop" title="折叠/展开">
{{ isTopCollapsed ? '▼' : '▲' }}
</button>
</div>
<div v-show="!isTopCollapsed" class="section-content">
<component :is="recentTools[0]?.component" v-if="recentTools[0]" />
<EmptyState v-else message="点击上方标签选择工具" />
</div>
</div>
<!-- 右侧-下部分区次新使用的工具 -->
<div class="panel-section bottom-section">
<div class="section-header">
<span class="section-title">{{ recentTools[1]?.label || '未选择工具' }}</span>
<button class="collapse-btn" @click="toggleBottom" title="折叠/展开">
{{ isBottomCollapsed ? '▼' : '▲' }}
</button>
</div>
<div v-show="!isBottomCollapsed" class="section-content">
<component :is="recentTools[1]?.component" v-if="recentTools[1]" />
<EmptyState v-else message="点击上方标签选择工具" />
</div>
</div>
</aside>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import RightToolDiceView from '../features/right/DiceView.vue';
import RightToolRagView from '../features/right/RagView.vue';
import RightToolWorldBookCallView from '../features/right/WorldBookCallView.vue';
import RightToolDynamicTableView from '../features/right/DynamicTableView.vue';
import EmptyState from '../shared/EmptyState.vue';
interface ToolItem {
id: string;
label: string;
shortLabel: string;
icon: string;
component: any;
}
const isTopCollapsed = ref(false);
const isBottomCollapsed = ref(false);
const recentTools = ref<ToolItem[]>([]);
// 所有可用工具
const availableTools: Record<string, ToolItem> = {
dice: {
id: 'dice',
label: '🎲 骰子工具',
shortLabel: '骰子',
icon: '🎲',
component: RightToolDiceView,
},
rag: {
id: 'rag',
label: '🔍 RAG 检索',
shortLabel: 'RAG',
icon: '🔍',
component: RightToolRagView,
},
worldbook: {
id: 'worldbook',
label: '📚 世界书调用',
shortLabel: '世界书',
icon: '📚',
component: RightToolWorldBookCallView,
},
table: {
id: 'table',
label: '📊 动态表格',
shortLabel: '表格',
icon: '📊',
component: RightToolDynamicTableView,
},
};
// 获取工具列表(用于渲染标签)
const availableToolsList = Object.values(availableTools);
// 判断工具是否在当前显示的两个中
function isToolActive(toolId: string): boolean {
return recentTools.value.some((tool) => tool.id === toolId);
}
// 选择工具(添加到最近使用队列)
function selectTool(toolId: string) {
const tool = availableTools[toolId];
if (!tool) return;
// 移除已存在的相同工具
recentTools.value = recentTools.value.filter((t) => t.id !== toolId);
// 添加到最前面
recentTools.value.unshift(tool);
// 只保留最近两个
if (recentTools.value.length > 2) {
recentTools.value = recentTools.value.slice(0, 2);
}
}
function toggleTop() {
isTopCollapsed.value = !isTopCollapsed.value;
}
function toggleBottom() {
isBottomCollapsed.value = !isBottomCollapsed.value;
}
// 暴露方法供外部调用
defineExpose({
selectTool,
});
// 初始化默认工具
selectTool('dice');
selectTool('worldbook');
</script>
<style scoped>
.right-panel {
display: flex;
flex-direction: column;
width: 25%;
min-width: 300px;
max-width: 400px;
background: #ffffff;
border-left: 1px solid #e0e0e0;
flex-shrink: 0;
}
.panel-tabs {
display: flex;
background: #f8f9fa;
border-bottom: 1px solid #e0e0e0;
padding: 6px 8px;
gap: 4px;
flex-shrink: 0;
overflow-x: auto;
}
.tab-btn {
flex: 1;
min-width: 60px;
padding: 8px 6px;
border: 1px solid transparent;
border-radius: 4px;
background: transparent;
color: #7f8c8d;
cursor: pointer;
font-size: 11px;
font-weight: 500;
transition: all 0.2s;
display: flex;
flex-direction: column;
align-items: center;
gap: 2px;
white-space: nowrap;
}
.tab-btn:hover {
background: #ffffff;
border-color: #e0e0e0;
color: #2c3e50;
}
.tab-btn.active {
background: #ffffff;
border-color: #3498db;
color: #3498db;
box-shadow: 0 1px 3px rgba(52, 152, 219, 0.1);
}
.panel-section {
display: flex;
flex-direction: column;
flex: 1;
overflow: hidden;
border-bottom: 1px solid #e0e0e0;
}
.panel-section:last-child {
border-bottom: none;
}
.section-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 10px 16px;
background: #ffffff;
border-bottom: 1px solid #e0e0e0;
flex-shrink: 0;
}
.section-title {
font-size: 13px;
font-weight: 600;
color: #2c3e50;
}
.collapse-btn {
padding: 4px 8px;
border: none;
background: transparent;
cursor: pointer;
font-size: 12px;
color: #7f8c8d;
transition: color 0.2s;
border-radius: 3px;
}
.collapse-btn:hover {
color: #2c3e50;
background: #f8f9fa;
}
.section-content {
flex: 1;
overflow-y: auto;
padding: 12px;
background: #fafafa;
}
/* 滚动条样式 */
.panel-tabs::-webkit-scrollbar,
.section-content::-webkit-scrollbar {
width: 6px;
height: 6px;
}
.panel-tabs::-webkit-scrollbar-track,
.section-content::-webkit-scrollbar-track {
background: transparent;
}
.panel-tabs::-webkit-scrollbar-thumb,
.section-content::-webkit-scrollbar-thumb {
background: #d0d0d0;
border-radius: 3px;
}
.panel-tabs::-webkit-scrollbar-thumb:hover,
.section-content::-webkit-scrollbar-thumb:hover {
background: #b0b0b0;
}
</style>

View File

@@ -1,18 +0,0 @@
<template>
<div class="right-toolbar">
<span style="color: #bdc3c7; font-size: 12px;">工具栏</span>
</div>
</template>
<script setup lang="ts">
defineProps<{
rightPanelRef?: any;
}>();
</script>
<style scoped>
.right-toolbar {
display: flex;
gap: 4px;
}
</style>

View File

@@ -1,125 +0,0 @@
<template>
<header class="top-toolbar">
<div class="toolbar-left">
<!-- API 配置快速切换 -->
<div class="toolbar-item">
<span class="label">API:</span>
<select v-model="currentApi" class="toolbar-select">
<option value="primary"> API</option>
<option value="secondary"> API</option>
</select>
</div>
</div>
<div class="toolbar-center">
<!-- 当前用户角色 -->
<div class="toolbar-item">
<span class="label">用户:</span>
<span class="value">{{ currentUser }}</span>
</div>
<!-- 当前 AI 角色 -->
<div class="toolbar-item">
<span class="label">AI:</span>
<span class="value">{{ currentAICharacter }}</span>
</div>
<!-- 激活的全局世界书 -->
<div class="toolbar-item">
<span class="label">世界书:</span>
<span class="value">{{ activeWorldBooks.length }} 个激活</span>
</div>
</div>
<div class="toolbar-right">
<!-- 设置按钮 -->
<button class="toolbar-btn" @click="openSettings">
设置
</button>
<!-- 拓展按钮 -->
<button class="toolbar-btn" @click="openExtensions">
🧩 拓展
</button>
</div>
</header>
</template>
<script setup lang="ts">
import { ref } from 'vue';
const currentApi = ref('primary');
const currentUser = ref('User');
const currentAICharacter = ref('Assistant');
const activeWorldBooks = ref(['奇幻世界', '现代都市']);
function openSettings() {
console.log('Open settings');
}
function openExtensions() {
console.log('Open extensions');
}
</script>
<style scoped>
.top-toolbar {
display: flex;
align-items: center;
justify-content: space-between;
padding: 8px 16px;
background: #2c3e50;
color: white;
border-bottom: 2px solid #34495e;
height: 50px;
flex-shrink: 0;
}
.toolbar-left,
.toolbar-center,
.toolbar-right {
display: flex;
align-items: center;
gap: 16px;
}
.toolbar-item {
display: flex;
align-items: center;
gap: 8px;
}
.label {
font-size: 12px;
opacity: 0.8;
}
.value {
font-size: 14px;
font-weight: 500;
}
.toolbar-select {
padding: 4px 8px;
border-radius: 4px;
border: 1px solid #34495e;
background: #34495e;
color: white;
font-size: 13px;
}
.toolbar-btn {
padding: 6px 12px;
border: none;
border-radius: 4px;
background: #3498db;
color: white;
cursor: pointer;
font-size: 13px;
transition: background 0.2s;
}
.toolbar-btn:hover {
background: #2980b9;
}
</style>

View File

@@ -1,51 +0,0 @@
/**
* useApi Composable
* Provides API client with error handling and loading states
*/
import { ref } from 'vue';
import type { Ref } from 'vue';
interface UseApiOptions<T> {
immediate?: boolean;
onError?: (error: Error) => void;
onSuccess?: (data: T) => void;
}
export function useApi<T>(
apiCall: () => Promise<T>,
options: UseApiOptions<T> = {}
) {
const data: Ref<T | null> = ref(null);
const error: Ref<Error | null> = ref(null);
const loading: Ref<boolean> = ref(false);
const execute = async () => {
loading.value = true;
error.value = null;
try {
const result = await apiCall();
data.value = result;
options.onSuccess?.(result);
return result;
} catch (err) {
error.value = err instanceof Error ? err : new Error(String(err));
options.onError?.(error.value);
throw error.value;
} finally {
loading.value = false;
}
};
if (options.immediate) {
execute();
}
return {
data,
error,
loading,
execute,
};
}

View File

View File

@@ -1,14 +0,0 @@
import './assets/main.css'
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'
import router from './router'
const app = createApp(App)
app.use(createPinia())
app.use(router)
app.mount('#app')

View File

View File

View File

View File

View File

@@ -1,19 +0,0 @@
{
"extends": "@vue/tsconfig/tsconfig.dom.json",
"include": ["env.d.ts", "src/**/*", "src/**/*.vue"],
"exclude": ["src/**/__tests__/*"],
"compilerOptions": {
// Extra safety for array and object lookups, but may have false positives.
"noUncheckedIndexedAccess": true,
// Path mapping for cleaner imports.
"paths": {
"@/*": ["./src/*"],
"@shared/*": ["../shared/types/*"]
},
// `vue-tsc --build` produces a .tsbuildinfo file for incremental type-checking.
// Specified here to keep it out of the root directory.
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo"
}
}

View File

@@ -1,11 +0,0 @@
{
"files": [],
"references": [
{
"path": "./tsconfig.node.json"
},
{
"path": "./tsconfig.app.json"
}
]
}

View File

@@ -1,27 +0,0 @@
// TSConfig for modules that run in Node.js environment via either transpilation or type-stripping.
{
"extends": "@tsconfig/node24/tsconfig.json",
"include": [
"vite.config.*",
"vitest.config.*",
"cypress.config.*",
"playwright.config.*",
"eslint.config.*"
],
"compilerOptions": {
// Most tools use transpilation instead of Node.js's native type-stripping.
// Bundler mode provides a smoother developer experience.
"module": "preserve",
"moduleResolution": "bundler",
// Include Node.js types and avoid accidentally including other `@types/*` packages.
"types": ["node"],
// Disable emitting output during `vue-tsc --build`, which is used for type-checking only.
"noEmit": true,
// `vue-tsc --build` produces a .tsbuildinfo file for incremental type-checking.
// Specified here to keep it out of the root directory.
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo"
}
}

View File

@@ -1,23 +0,0 @@
import { fileURLToPath, URL } from 'node:url'
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
export default defineConfig({
plugins: [vue()],
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url)),
'@shared': fileURLToPath(new URL('../shared/types', import.meta.url))
}
},
server: {
port: 8080,
proxy: {
'/api': {
target: 'http://localhost:3000',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, '')
}
}
}
})

View File

@@ -1,68 +0,0 @@
version: '3.8'
services:
# Backend Service (NestJS)
backend:
build:
context: ./server
dockerfile: Dockerfile.dev
container_name: sillytavern-backend
restart: unless-stopped
ports:
- "23337:3000" # Map to host port 23337
volumes:
# Mount code for hot reload (development)
- ./server:/usr/src/app
- ./shared:/usr/src/shared
# Exclude node_modules to use container's version
- /usr/src/app/node_modules
# Persistent data
- ./data:/usr/src/app/data
- ./.env:/usr/src/app/.env:ro
environment:
- NODE_ENV=development
- PORT=3000
- DB_DATABASE=./data/db/app.db
- UPLOAD_DIR=./data/files
- LOG_DIR=./data/logs
working_dir: /usr/src/app
command: npm run start:dev
networks:
- app-network
healthcheck:
test: ["CMD", "wget", "--spider", "-q", "http://localhost:3000/api"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
# Frontend Service (Vue3 + Vite)
frontend:
build:
context: ./client
dockerfile: Dockerfile.dev
container_name: sillytavern-frontend
restart: unless-stopped
ports:
- "23338:5173" # Vite dev server port
volumes:
# Mount code for hot reload (development)
- ./client:/usr/src/app
- ./shared:/usr/src/shared
# Exclude node_modules
- /usr/src/app/node_modules
environment:
- NODE_ENV=development
- VITE_API_URL=http://localhost:23337
- VITE_WS_URL=ws://localhost:23337
working_dir: /usr/src/app
command: npm run dev -- --host 0.0.0.0 --port 5173
depends_on:
backend:
condition: service_healthy
networks:
- app-network
networks:
app-network:
driver: bridge

View File

View File

View File

310
make.bat
View File

@@ -1,310 +0,0 @@
NEW_FILE_CODE
@echo off
setlocal enabledelayedexpansion
echo =====================================================
echo Creating Enterprise-Grade Project Architecture...
echo Backend: NestJS with DDD + Frontend: Vue3 Composition API
echo =====================================================
:: Check if we are in the right directory
if not exist server (
echo [ERROR] Please run this script from project root directory
pause
exit /b
)
:: -------------------------------------------------------
:: 1. Restructure Backend (NestJS)
:: -------------------------------------------------------
echo.
echo [Backend] Creating complete directory structure...
cd server\src
:: Remove existing directories to start fresh
if exist core rmdir /s /q core
if exist modules rmdir /s /q modules
if exist shared rmdir /s /q shared
:: === CORE LAYER ===
echo Creating core layer...
mkdir core
mkdir core\config
mkdir core\database
mkdir core\database\migrations
mkdir core\database\seeds
mkdir core\decorators
mkdir core\filters
mkdir core\guards
mkdir core\interceptors
mkdir core\pipes
mkdir core\middleware
:: === MODULES LAYER ===
echo Creating modules layer...
mkdir modules
:: Auth Module
mkdir modules\auth
mkdir modules\auth\controllers
mkdir modules\auth\services
mkdir modules\auth\strategies
mkdir modules\auth\dto
mkdir modules\auth\interfaces
:: LLM Module
mkdir modules\llm
mkdir modules\llm\controllers
mkdir modules\llm\services
mkdir modules\llm\providers
mkdir modules\llm\dto
mkdir modules\llm\entities
mkdir modules\llm\interfaces
:: Workflow Module
mkdir modules\workflow
mkdir modules\workflow\controllers
mkdir modules\workflow\services
mkdir modules\workflow\engine
mkdir modules\workflow\nodes
mkdir modules\workflow\dto
mkdir modules\workflow\entities
mkdir modules\workflow\interfaces
:: Data Store Module
mkdir modules\data-store
mkdir modules\data-store\controllers
mkdir modules\data-store\services
mkdir modules\data-store\repositories
mkdir modules\data-store\entities
mkdir modules\data-store\schemas
mkdir modules\data-store\dto
mkdir modules\data-store\interfaces
:: Import/Export Module
mkdir modules\import-export
mkdir modules\import-export\controllers
mkdir modules\import-export\services
mkdir modules\import-export\transformers
mkdir modules\import-export\serializers
mkdir modules\import-export\parsers
mkdir modules\import-export\dto
mkdir modules\import-export\interfaces
:: File Management Module
mkdir modules\file-management
mkdir modules\file-management\controllers
mkdir modules\file-management\services
mkdir modules\file-management\storage
mkdir modules\file-management\dto
mkdir modules\file-management\entities
:: === SHARED LAYER ===
echo Creating shared layer...
mkdir shared
mkdir shared\interfaces
mkdir shared\types
mkdir shared\utils
mkdir shared\constants
mkdir shared\dtos
mkdir shared\decorators
:: === EVENTS SYSTEM ===
echo Creating events system...
mkdir events
mkdir events\event-emitter
mkdir events\events
mkdir events\listeners
:: === QUEUES SYSTEM ===
echo Creating queues system...
mkdir queues
mkdir queues\processors
mkdir queues\jobs
mkdir queues\interfaces
:: === COMMON LAYER ===
echo Creating common layer...
mkdir common
mkdir common\base
mkdir common\mixins
mkdir common\exceptions
cd ..\..
echo [Backend] Directory structure created successfully!
:: -------------------------------------------------------
:: 2. Restructure Frontend (Vue3)
:: -------------------------------------------------------
echo.
echo [Frontend] Creating complete directory structure...
cd client\src
:: Remove existing directories to start fresh
if exist api rmdir /s /q api
if exist components rmdir /s /q components
if exist views rmdir /s /q views
if exist composables rmdir /s /q composables
if exist stores rmdir /s /q stores
if exist router rmdir /s /q router
if exist types rmdir /s /q types
if exist utils rmdir /s /q utils
:: === API LAYER ===
echo Creating API layer...
mkdir api
mkdir api\clients
mkdir api\modules
mkdir api\interceptors
:: === COMPONENTS LAYER ===
echo Creating components layer...
mkdir components
mkdir components\base
mkdir components\base\buttons
mkdir components\base\inputs
mkdir components\base\modals
mkdir components\base\layouts
mkdir components\base\tables
mkdir components\base\forms
mkdir components\business
mkdir components\business\llm-chat
mkdir components\business\workflow-canvas
mkdir components\business\data-table
mkdir components\business\file-uploader
mkdir components\business\prompt-editor
mkdir components\business\node-palette
mkdir components\widgets
mkdir components\widgets\status-indicator
mkdir components\widgets\progress-bar
mkdir components\widgets\toast
:: === VIEWS LAYER ===
echo Creating views layer...
mkdir views
mkdir views\dashboard
mkdir views\workflow-editor
mkdir views\llm-playground
mkdir views\data-browser
mkdir views\settings
mkdir views\auth
:: === COMPOSABLES LAYER ===
echo Creating composables layer...
mkdir composables
:: === STORES LAYER ===
echo Creating stores layer...
mkdir stores
mkdir stores\modules
:: === ROUTER LAYER ===
echo Creating router layer...
mkdir router
mkdir router\routes
:: === TYPES LAYER ===
echo Creating types layer...
mkdir types
mkdir types\models
mkdir types\api
mkdir types\common
:: === UTILS LAYER ===
echo Creating utils layer...
mkdir utils
mkdir utils\formatters
mkdir utils\validators
mkdir utils\helpers
mkdir utils\constants
:: === ASSETS LAYER ===
echo Creating assets structure...
if not exist assets mkdir assets
mkdir assets\styles
mkdir assets\icons
mkdir assets\images
:: === PLUGINS LAYER ===
echo Creating plugins layer...
mkdir plugins
cd ..\..
echo [Frontend] Directory structure created successfully!
:: -------------------------------------------------------
:: 3. Create Root Level Directories
:: -------------------------------------------------------
echo.
echo [Infrastructure] Creating infrastructure directories...
if not exist data mkdir data
if not exist data\db mkdir data\db
if not exist data\files mkdir data\files
if not exist data\logs mkdir data\logs
if not exist data\backups mkdir data\backups
if not exist docker mkdir docker
if not exist docker\nginx mkdir docker\nginx
if not exist docker\app mkdir docker\app
if not exist docker\certs mkdir docker\certs
if not exist docs mkdir docs
if not exist scripts mkdir scripts
if not exist tests mkdir tests
echo [Infrastructure] Infrastructure directories created!
:: -------------------------------------------------------
:: 4. Summary
:: -------------------------------------------------------
echo.
echo =====================================================
echo Project architecture created successfully!
echo.
echo Backend Structure (server/src):
echo - core/ Infrastructure layer
echo - modules/ Business modules (DDD)
echo - shared/ Shared utilities
echo - events/ Event system
echo - queues/ Task queues
echo - common/ Base classes
echo.
echo Frontend Structure (client/src):
echo - api/ API clients
echo - components/ UI components
echo - views/ Page views
echo - composables/ Reusable logic
echo - stores/ State management
echo - types/ TypeScript types
echo - utils/ Utility functions
echo.
echo Next steps:
echo 1. cd server ^&^& npm install
echo 2. cd ../client ^&^& npm install
echo 3. Start implementing base classes and interfaces
echo =====================================================
pause

View File

@@ -1,6 +0,0 @@
node_modules
dist
.env
.git
.idea
.vscode

View File

@@ -1,57 +0,0 @@
# =====================================================
# Server Configuration
# =====================================================
NODE_ENV=development
PORT=3000
API_PREFIX=/api
# =====================================================
# Database Configuration (File-based, no DB server needed)
# =====================================================
DB_TYPE=better-sqlite3
DB_DATABASE=./data/db/app.db
# =====================================================
# JWT Authentication
# =====================================================
JWT_SECRET=change-this-to-a-random-string-at-least-32-chars
JWT_EXPIRES_IN=7d
JWT_REFRESH_SECRET=change-this-to-another-random-string
JWT_REFRESH_EXPIRES_IN=30d
# =====================================================
# LLM Provider API Keys (Backend Only - Secure)
# =====================================================
OPENAI_API_KEY=sk-your-openai-api-key-here
ANTHROPIC_API_KEY=sk-ant-your-anthropic-api-key-here
GOOGLE_API_KEY=your-google-api-key-here
LOCAL_LLM_URL=http://localhost:11434
# =====================================================
# File Storage
# =====================================================
UPLOAD_DIR=./data/files
MAX_FILE_SIZE=10485760
# =====================================================
# CORS Configuration
# =====================================================
CORS_ORIGIN=http://localhost:23338
# =====================================================
# Rate Limiting
# =====================================================
RATE_LIMIT_TTL=60
RATE_LIMIT_MAX=100
# =====================================================
# Workflow Configuration
# =====================================================
WORKFLOW_MAX_PARALLEL=5
WORKFLOW_TIMEOUT=300000
# =====================================================
# Logging
# =====================================================
LOG_LEVEL=debug
LOG_DIR=./data/logs

Some files were not shown because too many files have changed in this diff Show More