├── .cursor └── rules │ └── all.mdc ├── .dockerignore ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── custom.md │ └── feature_request.md └── workflows │ └── docker-image.yml ├── .gitignore ├── CODE_OF_CONDUCT.md ├── KoalaWiki.Core ├── DataAccess │ ├── IKoalaWikiContext.cs │ ├── IUserContext.cs │ └── KoalaWikiContext.cs ├── KoalaWiki.Core.csproj └── ServiceExtensions.cs ├── KoalaWiki.Domains ├── ChatShareMessage.cs ├── ChatShareMessageItem.cs ├── Document.cs ├── DocumentCatalog.cs ├── DocumentCommitRecord.cs ├── DocumentFile │ ├── DocumentFileItem.cs │ └── DocumentFileItemSource.cs ├── DocumentOverview.cs ├── Entity.cs ├── FineTuning │ ├── FineTuningTask.cs │ ├── FineTuningTaskStatus.cs │ ├── TrainingDataset.cs │ └── TrainingDatasetStatus.cs ├── ICreateEntity.cs ├── IEntity.cs ├── KoalaWiki.Domains.csproj ├── MCP │ └── MCPHistory.cs ├── Users │ └── User.cs ├── Warehouse.cs └── WarehouseStatus.cs ├── KoalaWiki.sln ├── LICENSE ├── Makefile ├── NuGet.Config ├── Provider ├── KoalaWiki.Provider.PostgreSQL │ ├── KoalaWiki.Provider.PostgreSQL.csproj │ ├── Migrations │ │ ├── 20250429184845_Initial.Designer.cs │ │ ├── 20250429184845_Initial.cs │ │ ├── 20250430080021_AddDocumentCommitRecords.Designer.cs │ │ ├── 20250430080021_AddDocumentCommitRecords.cs │ │ ├── 20250430094304_AddGit.Designer.cs │ │ ├── 20250430094304_AddGit.cs │ │ ├── 20250430095405_AddEmail.Designer.cs │ │ ├── 20250430095405_AddEmail.cs │ │ ├── 20250506101108_AddEmbedded.Designer.cs │ │ ├── 20250506101108_AddEmbedded.cs │ │ ├── 20250506173606_AddChatMessage.Designer.cs │ │ ├── 20250506173606_AddChatMessage.cs │ │ ├── 20250506195024_AddQuestion.Designer.cs │ │ ├── 20250506195024_AddQuestion.cs │ │ ├── 20250507090410_AddFile.Designer.cs │ │ ├── 20250507090410_AddFile.cs │ │ ├── 20250508100054_AddRecommended.Designer.cs │ │ ├── 20250508100054_AddRecommended.cs │ │ ├── 20250509063050_AddIsCompleted.Designer.cs │ │ ├── 20250509063050_AddIsCompleted.cs │ │ ├── 20250510081053_AddFiles.Designer.cs │ │ ├── 20250510081053_AddFiles.cs │ │ ├── 20250510210307_AddReadme.Designer.cs │ │ ├── 20250510210307_AddReadme.cs │ │ ├── 20250512065940_AddDeletedTime.Designer.cs │ │ ├── 20250512065940_AddDeletedTime.cs │ │ ├── 20250512154412_AddCommitTitle.Designer.cs │ │ ├── 20250512154412_AddCommitTitle.cs │ │ ├── 20250515170036_AddUser.Designer.cs │ │ ├── 20250515170036_AddUser.cs │ │ ├── 20250521194134_AddFineTuning.Designer.cs │ │ ├── 20250521194134_AddFineTuning.cs │ │ ├── 20250523102229_AddMCPHistory.Designer.cs │ │ ├── 20250523102229_AddMCPHistory.cs │ │ └── PostgreSQLContextModelSnapshot.cs │ ├── PostgreSQLApplicationExtensions.cs │ └── PostgreSQLContext.cs ├── KoalaWiki.Provider.SqlServer │ ├── KoalaWiki.Provider.SqlServer.csproj │ ├── Migrations │ │ ├── 20250513064801_Initial.Designer.cs │ │ ├── 20250513064801_Initial.cs │ │ ├── 20250515170141_AddUser.Designer.cs │ │ ├── 20250515170141_AddUser.cs │ │ ├── 20250521194214_AddFineTuning.Designer.cs │ │ ├── 20250521194214_AddFineTuning.cs │ │ ├── 20250523102304_AddMCPHistory.Designer.cs │ │ ├── 20250523102304_AddMCPHistory.cs │ │ └── SqlServerContextModelSnapshot.cs │ ├── SqlServerApplicationExtensions.cs │ └── SqlServerContext.cs └── KoalaWiki.Provider.Sqlite │ ├── KoalaWiki.Provider.Sqlite.csproj │ ├── Migrations │ ├── 20250429184747_Initial.Designer.cs │ ├── 20250429184747_Initial.cs │ ├── 20250430075934_AddDocumentCommitRecords.Designer.cs │ ├── 20250430075934_AddDocumentCommitRecords.cs │ ├── 20250430094200_AddGitUser.Designer.cs │ ├── 20250430094200_AddGitUser.cs │ ├── 20250430095428_AddEmail.Designer.cs │ ├── 20250430095428_AddEmail.cs │ ├── 20250506095725_AddEmbedded.Designer.cs │ ├── 20250506095725_AddEmbedded.cs │ ├── 20250506173447_AddChatMessage.Designer.cs │ ├── 20250506173447_AddChatMessage.cs │ ├── 20250506194943_AddQuestion.Designer.cs │ ├── 20250506194943_AddQuestion.cs │ ├── 20250507090336_AddFile.Designer.cs │ ├── 20250507090336_AddFile.cs │ ├── 20250508100013_addRecommended.Designer.cs │ ├── 20250508100013_addRecommended.cs │ ├── 20250509063114_AddIsCompleted.Designer.cs │ ├── 20250509063114_AddIsCompleted.cs │ ├── 20250510081022_AddFiles.Designer.cs │ ├── 20250510081022_AddFiles.cs │ ├── 20250510192355_AddReadme.Designer.cs │ ├── 20250510192355_AddReadme.cs │ ├── 20250512065649_AddDeletedTime.Designer.cs │ ├── 20250512065649_AddDeletedTime.cs │ ├── 20250512093510_AddCommitTitle.Designer.cs │ ├── 20250512093510_AddCommitTitle.cs │ ├── 20250515170011_AddUser.Designer.cs │ ├── 20250515170011_AddUser.cs │ ├── 20250521174116_AddFineTuning.Designer.cs │ ├── 20250521174116_AddFineTuning.cs │ ├── 20250521190105_AddModel.Designer.cs │ ├── 20250521190105_AddModel.cs │ ├── 20250523101910_AddMCPHistory.Designer.cs │ ├── 20250523101910_AddMCPHistory.cs │ └── SqliteContextModelSnapshot.cs │ ├── SqliteApplicationExtensions.cs │ └── SqliteContext.cs ├── README.md ├── README.zh-CN.md ├── build-image.bat ├── build-image.sh ├── build.bat ├── build.sh ├── docker-compose.yml ├── img ├── favicon.png ├── mcp.png └── sealos │ ├── sealos-deploy.jpg │ ├── sealos-details.jpg │ ├── sealos-index.jpg │ ├── sealos-myapps.jpg │ ├── sealos-online-debug.jpg │ ├── sealos-overview.jpg │ ├── sealos-test.jpg │ └── sealos-web.jpg ├── nginx └── nginx.conf ├── package-lock.json ├── package.json ├── scripts └── sealos │ ├── README.zh-CN.md │ └── sealos-template.yaml ├── src └── KoalaWiki │ ├── CodeMap │ ├── BuildCodeIndex.cs │ ├── CodeMap.cs │ ├── CodeSegment.cs │ └── EnhancedCodeIndexer.cs │ ├── Constant.cs │ ├── DataMigration │ └── DataMigrationTask.cs │ ├── Dockerfile │ ├── Dto │ ├── ChatShareMessageInput.cs │ ├── CompletionsInput.cs │ ├── CreateCatalogInput.cs │ ├── FineTuningDto.cs │ ├── GenerateFileContentInput.cs │ ├── LoginDto.cs │ ├── PageDto.cs │ ├── RegisterInput.cs │ ├── RepoExtendedInfo.cs │ ├── RepoGiteeRepoDto.cs │ ├── RepoGithubRepoDto.cs │ ├── RepositoryDto.cs │ ├── ResultDto.cs │ ├── SaveFileContentInput.cs │ ├── StartTaskInput.cs │ ├── UserDto.cs │ ├── WarehouseDto.cs │ └── WarehouseInput.cs │ ├── Extensions │ ├── DbContextExtensions.cs │ ├── GlobalMiddleware.cs │ ├── SitemapExtensions.cs │ └── StringExtensions.cs │ ├── Functions │ ├── FileFunction.cs │ └── RagFunction.cs │ ├── Git │ ├── GitRepositoryInfo.cs │ └── GitService.cs │ ├── GlobalUsing.cs │ ├── Infrastructure │ ├── ResultFilter.cs │ └── UserContext.cs │ ├── KernelFactory.cs │ ├── KoalaHttpClientHander.cs │ ├── KoalaWarehouse │ ├── DocumentContext.cs │ ├── DocumentResultCatalogue.cs │ ├── DocumentsService.Catalogue.cs │ ├── DocumentsService.Commit.cs │ ├── DocumentsService.DocumentPending.cs │ ├── DocumentsService.cs │ ├── PathInfo.cs │ ├── Prompt.cs │ └── WarehouseTask.cs │ ├── KoalaWiki.csproj │ ├── KoalaWiki.sln │ ├── MCP │ ├── MCPExtensions.cs │ └── Tools │ │ └── WarehouseTool.cs │ ├── Options │ ├── DocumentOptions.cs │ ├── GithubOptions.cs │ ├── JwtOptions.cs │ └── OpenAIOptions.cs │ ├── Program.cs │ ├── Properties │ └── launchSettings.json │ ├── Services │ ├── AuthService.cs │ ├── ChatService.cs │ ├── ChatShareMessageService.cs │ ├── DocumentCatalogService.cs │ ├── FineTuningService.cs │ ├── GitRepositoryService.cs │ ├── RepositoryService.cs │ ├── UserService.cs │ └── WarehouseService.cs │ ├── WarehouseProcessing │ ├── WarehouseDescriptionTask.cs │ ├── WarehouseFunctionPromptTask.cs │ ├── WarehouseProcessingTask.Analyse.cs │ ├── WarehouseProcessingTask.Commit.cs │ └── WarehouseProcessingTask.cs │ ├── appsettings.Development.json │ ├── appsettings.json │ └── plugins │ ├── CodeAnalysis │ ├── CodeDirSimplifier │ │ ├── config.json │ │ └── skprompt.txt │ ├── CommitAnalyze │ │ ├── config.json │ │ └── skprompt.txt │ ├── FunctionPrompt │ │ ├── config.json │ │ └── skprompt.txt │ ├── GenerateDescription │ │ ├── config.json │ │ └── skprompt.txt │ └── GenerateReadme │ │ ├── config.json │ │ └── skprompt.txt │ └── LanguagePromptFilter.cs ├── start-backend.bat ├── start-backend.sh ├── start-frontend.bat ├── start-frontend.sh └── web ├── .dockerignore ├── .env.samples ├── .npmrc ├── .trae └── rules │ └── project_rules.md ├── .vscode └── settings.json ├── Dockerfile ├── README.md ├── app ├── [owner] │ ├── [name] │ │ ├── ClientRepositoryPage.tsx │ │ ├── RepositoryInfo.tsx │ │ ├── RepositoryView.tsx │ │ ├── [path] │ │ │ ├── layout.tsx │ │ │ └── page.tsx │ │ ├── changelog │ │ │ └── page.tsx │ │ ├── layout.client.tsx │ │ ├── layout.server.tsx │ │ ├── layout.tsx │ │ └── page.tsx │ └── page.tsx ├── admin │ ├── components │ │ ├── Header.tsx │ │ └── Sidebar.tsx │ ├── finetune │ │ ├── create │ │ │ └── page.tsx │ │ ├── dataset │ │ │ └── [id] │ │ │ │ ├── edit │ │ │ │ └── page.tsx │ │ │ │ └── page.tsx │ │ └── page.tsx │ ├── layout.tsx │ ├── page.tsx │ ├── pages │ │ └── Dashboard.tsx │ ├── repositories │ │ ├── [id] │ │ │ └── page.tsx │ │ └── page.tsx │ ├── settings │ │ └── page.tsx │ ├── styles.module.css │ ├── test-auth.md │ └── users │ │ └── page.tsx ├── components │ ├── AIInputBar.tsx │ ├── DirectoryTree.tsx │ ├── DocumentToc.tsx │ ├── HomeClient.tsx │ ├── LanguageSwitcher.tsx │ ├── LastRepoModal.tsx │ ├── MarkdownEditor.tsx │ ├── RepositoryCard.tsx │ ├── RepositoryForm.tsx │ ├── RepositoryList.tsx │ └── document │ │ ├── AntThinking.tsx │ │ ├── ChangelogContent.tsx │ │ ├── Component.tsx │ │ ├── DocumentContent.tsx │ │ ├── DocumentHeader.tsx │ │ ├── DocumentSidebar.css │ │ ├── DocumentSidebar.tsx │ │ ├── DocumentStyles.tsx │ │ ├── LoadingErrorState.tsx │ │ ├── MarkdownElements.ts │ │ ├── MobileDocumentDrawer.tsx │ │ ├── ServerComponents.tsx │ │ ├── SourceFiles.tsx │ │ ├── index.ts │ │ ├── rehypePlugin.ts │ │ ├── thinking │ │ └── remarkPlugin.ts │ │ └── utils │ │ ├── headingUtils.ts │ │ └── mermaidUtils.ts ├── const │ └── urlconst.ts ├── globals.css ├── i18n │ ├── client.ts │ ├── server.ts │ └── settings.ts ├── layout.tsx ├── login │ ├── auth.module.css │ └── page.tsx ├── page.module.css ├── page.tsx ├── privacy │ └── page.tsx ├── register │ └── page.tsx ├── search │ └── [query] │ │ └── page.tsx ├── services │ ├── api.ts │ ├── authService.ts │ ├── chatShareMessageServce.ts │ ├── documentService.ts │ ├── fetchApi.ts │ ├── fineTuningService.ts │ ├── githubService.ts │ ├── index.ts │ ├── openaiService.ts │ ├── repositoryService.ts │ ├── statsService.ts │ ├── userService.ts │ └── warehouseService.ts ├── sitemap.ts ├── terms │ └── page.tsx ├── types.ts └── types │ └── index.ts ├── bun.lock ├── components ├── Header.tsx └── Sidebar.tsx ├── layout.tsx ├── next-env.d.ts ├── next.config.js ├── package-lock.json ├── package.json ├── postcss.config.js ├── public ├── favicon.ico ├── favicon.png ├── locales │ ├── ar │ │ └── common.json │ ├── de │ │ └── common.json │ ├── en-US │ │ └── common.json │ ├── es │ │ └── common.json │ ├── fr │ │ └── common.json │ ├── hi │ │ └── common.json │ ├── id │ │ └── common.json │ ├── it │ │ └── common.json │ ├── ja │ │ └── common.json │ ├── ko │ │ └── common.json │ ├── nl │ │ └── common.json │ ├── pt │ │ └── common.json │ ├── ru │ │ └── common.json │ ├── th │ │ └── common.json │ ├── tr │ │ └── common.json │ ├── vi │ │ └── common.json │ ├── zh-CN │ │ └── common.json │ └── zh-TW │ │ └── common.json ├── logo.png ├── mcp.png └── robots.txt ├── start.sh ├── styles.module.css ├── tailwind.config.js ├── tsconfig.json └── tsconfig.tsbuildinfo /.cursor/rules/all.mdc: -------------------------------------------------------------------------------- 1 | --- 2 | description: 3 | globs: 4 | alwaysApply: true 5 | --- 6 | 7 | # Ant Design UI Expert: React Frontend Development System 8 | 9 | ## ROLE DEFINITION 10 | You are an elite React UI designer with specialized expertise in Ant Design implementation. Your core competencies include: 11 | - Creating intuitive, aesthetically pleasing interfaces using Ant Design principles 12 | - Implementing responsive designs across all device types 13 | - Applying token-based theming for consistent styling 14 | - Following Ant Design best practices for optimal user experience 15 | 16 | ## TASK WORKFLOW 17 | When presented with a design task: 18 | 19 | 1. **Analysis Phase** 20 | * Identify key requirements and objectives 21 | * Determine relevant Ant Design components and principles 22 | * Assess cross-device adaptation requirements 23 | 24 | 2. **Planning Phase** 25 | * Outline implementation steps 26 | * Select appropriate Ant Design components and layout structures 27 | * Plan responsive implementation using Grid system and breakpoints 28 | * Develop token-based theming strategy 29 | 30 | ## DELIVERY STRUCTURE 31 | Provide your complete solution in the following format: 32 | 33 | 34 | 1. Task Title: [Concise description of objective] 35 | 36 | 2. Task Description: 37 | • Execution Plan: [3+ steps for designing with Ant Design components] 38 | • Design Principles: [3-5 core design principles for this task] 39 | • UI Integration: [2+ points on coordinating with existing interface] 40 | • Adaptive Strategy: [3+ points on responsive implementation using Ant Design breakpoints] 41 | 42 | 3. Expected Outcomes: [3-5 specific deliverables, including device-specific variations] 43 | 44 | 4. Evaluation Criteria: [3-5 metrics to measure success, including responsive performance] 45 | 46 | 5. Device Testing Plan: [Specific devices and breakpoints to be tested] 47 | 48 | 6. Implementation Notes: 49 | • Theme Configuration: [Specific Ant Design tokens for colors and typography] 50 | • Component Selection: [Key Ant Design components recommended] 51 | • Responsive Approach: [Application of Ant Design's Grid and responsive utilities] 52 | 53 | 54 | ## IMPLEMENTATION GUIDELINES 55 | - Use Ant Design's token system exclusively for colors and typography 56 | - Never use fixed color parameters 57 | - Implement responsive design using Ant Design's breakpoint system 58 | - Ensure seamless functionality across all device types 59 | - Reference official Ant Design documentation for component implementation 60 | - Maintain consistent user experience through proper component usage 61 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | **/.dockerignore 2 | **/.env 3 | **/.git 4 | **/.gitignore 5 | **/.project 6 | **/.settings 7 | **/.toolstarget 8 | **/.vs 9 | **/.vscode 10 | **/.idea 11 | **/*.*proj.user 12 | **/*.dbmdl 13 | **/*.jfm 14 | **/azds.yaml 15 | **/bin 16 | **/charts 17 | **/docker-compose* 18 | **/Dockerfile* 19 | **/node_modules 20 | **/npm-debug.log 21 | **/obj 22 | **/secrets.dev.yaml 23 | **/values.dev.yaml 24 | LICENSE 25 | README.md 26 | web -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/custom.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Custom issue template 3 | about: Describe this issue template's purpose here. 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | 11 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/workflows/docker-image.yml: -------------------------------------------------------------------------------- 1 | name: Build and Push Docker Images 2 | on: 3 | workflow_dispatch: 4 | push: 5 | branches: [ main ] 6 | jobs: 7 | build: 8 | strategy: 9 | matrix: 10 | image: [ 11 | { name: 'koala-wiki', type: 'backend', dockerfile: 'src/KoalaWiki/Dockerfile', context: '.' }, 12 | { name: 'koala-wiki-web', type: 'frontend', dockerfile: 'web/Dockerfile', context: 'web' } 13 | ] 14 | fail-fast: false 15 | runs-on: ubuntu-latest 16 | steps: 17 | - uses: actions/checkout@v4 18 | 19 | - name: Set up Docker Buildx 20 | uses: docker/setup-buildx-action@v3 21 | 22 | - name: Login to Docker Registry 23 | uses: docker/login-action@v3 24 | with: 25 | registry: crpi-j9ha7sxwhatgtvj4.cn-shenzhen.personal.cr.aliyuncs.com 26 | username: ${{ secrets.DOCKER_USERNAME }} 27 | password: ${{ secrets.DOCKER_PASSWORD }} 28 | 29 | - name: Build and push ${{ matrix.image.name }} 30 | uses: docker/build-push-action@v5 31 | with: 32 | context: ${{ matrix.image.context }} 33 | file: ${{ matrix.image.dockerfile }} 34 | platforms: linux/arm64,linux/amd64 35 | push: true 36 | tags: crpi-j9ha7sxwhatgtvj4.cn-shenzhen.personal.cr.aliyuncs.com/koala-ai/${{ matrix.image.name }}${{ matrix.image.tag || '' }} 37 | -------------------------------------------------------------------------------- /KoalaWiki.Core/DataAccess/IKoalaWikiContext.cs: -------------------------------------------------------------------------------- 1 | using System.Threading; 2 | using System.Threading.Tasks; 3 | using KoalaWiki.Domains; 4 | using KoalaWiki.Domains.FineTuning; 5 | using KoalaWiki.Domains.MCP; 6 | using KoalaWiki.Domains.Users; 7 | using KoalaWiki.Entities; 8 | using KoalaWiki.Entities.DocumentFile; 9 | using Microsoft.EntityFrameworkCore; 10 | 11 | namespace KoalaWiki.Core.DataAccess; 12 | 13 | public interface IKoalaWikiContext 14 | { 15 | public DbSet Warehouses { get; set; } 16 | 17 | public DbSet DocumentCatalogs { get; set; } 18 | 19 | public DbSet Documents { get; set; } 20 | 21 | public DbSet DocumentFileItems { get; set; } 22 | 23 | public DbSet DocumentFileItemSources { get; set; } 24 | 25 | public DbSet DocumentOverviews { get; set; } 26 | 27 | public DbSet DocumentCommitRecords { get; set; } 28 | 29 | public DbSet ChatShareMessages { get; set; } 30 | 31 | public DbSet ChatShareMessageItems { get; set; } 32 | 33 | /// 34 | /// 训练数据集 35 | /// 36 | public DbSet TrainingDatasets { get; set; } 37 | 38 | public DbSet FineTuningTasks { get; set; } 39 | 40 | public DbSet Users { get; set; } 41 | 42 | public DbSet MCPHistories { get; set; } 43 | 44 | Task SaveChangesAsync(CancellationToken cancellationToken = new CancellationToken()); 45 | 46 | Task RunMigrateAsync(); 47 | } -------------------------------------------------------------------------------- /KoalaWiki.Core/DataAccess/IUserContext.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace KoalaWiki.Core.DataAccess; 4 | 5 | /// 6 | /// 用户上下文接口 7 | /// 8 | public interface IUserContext 9 | { 10 | /// 11 | /// 获取当前用户ID 12 | /// 13 | string? CurrentUserId { get; } 14 | 15 | /// 16 | /// 获取当前用户名 17 | /// 18 | string? CurrentUserName { get; } 19 | 20 | /// 21 | /// 获取当前用户邮箱 22 | /// 23 | string? CurrentUserEmail { get; } 24 | 25 | /// 26 | /// 获取当前用户角色 27 | /// 28 | string? CurrentUserRole { get; } 29 | 30 | /// 31 | /// 判断用户是否已认证 32 | /// 33 | bool IsAuthenticated { get; } 34 | 35 | /// 36 | /// 判断用户是否是管理员 37 | /// 38 | bool IsAdmin { get; } 39 | } -------------------------------------------------------------------------------- /KoalaWiki.Core/KoalaWiki.Core.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net9.0 5 | latest 6 | enable 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /KoalaWiki.Core/ServiceExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using KoalaWiki.Core.DataAccess; 3 | using Microsoft.EntityFrameworkCore; 4 | using Microsoft.Extensions.DependencyInjection; 5 | 6 | namespace KoalaWiki.Core; 7 | 8 | public static class ServiceExtensions 9 | { 10 | public static IServiceCollection AddDataAccess(this IServiceCollection services, 11 | Action configureContext) where TContext : KoalaWikiContext 12 | { 13 | services.AddDbContext(configureContext); 14 | return services; 15 | } 16 | } -------------------------------------------------------------------------------- /KoalaWiki.Domains/ChatShareMessage.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.ComponentModel.DataAnnotations.Schema; 3 | 4 | namespace KoalaWiki.Domains; 5 | 6 | public class ChatShareMessage : Entity 7 | { 8 | /// 9 | /// 关联id 10 | /// 11 | public string WarehouseId { get; set; } = string.Empty; 12 | 13 | /// 14 | /// 是否深度推理 15 | /// 16 | /// 17 | public bool IsDeep { get; set; } = false; 18 | 19 | /// 20 | /// 标题 21 | /// 22 | public string Title { get; set; } = string.Empty; 23 | 24 | /// 25 | /// 请求ip 26 | /// 27 | public string Ip { get; set; } = string.Empty; 28 | 29 | /// 30 | /// 最开始的问题 31 | /// 32 | public string Question { get; set; } = string.Empty; 33 | 34 | [NotMapped] 35 | public List Items { get; set; } = new(); 36 | } 37 | 38 | -------------------------------------------------------------------------------- /KoalaWiki.Domains/ChatShareMessageItem.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using KoalaWiki.Entities; 3 | 4 | namespace KoalaWiki.Domains; 5 | 6 | public class ChatShareMessageItem : Entity 7 | { 8 | /// 9 | /// 关联id 10 | /// 11 | public string ChatShareMessageId { get; set; } = string.Empty; 12 | 13 | /// 14 | /// 仓库id 15 | /// 16 | public string WarehouseId { get; set; } = string.Empty; 17 | 18 | /// 19 | /// 问题内容 20 | /// 21 | public string Question { get; set; } = string.Empty; 22 | 23 | /// 24 | /// 回答内容 25 | /// 26 | public string Answer { get; set; } = string.Empty; 27 | 28 | /// 29 | /// 思考内容 30 | /// 31 | public string Think { get; set; } = string.Empty; 32 | 33 | /// 34 | /// 请求token 35 | /// 36 | public int PromptToken { get; set; } 37 | 38 | /// 39 | /// 完成token 40 | /// 41 | public int CompletionToken { get; set; } 42 | 43 | /// 44 | /// 总耗时 45 | /// 46 | public int TotalTime { get; set; } 47 | 48 | public List Files { get; set; } = new List(); 49 | } -------------------------------------------------------------------------------- /KoalaWiki.Domains/Document.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using KoalaWiki.Entities; 3 | 4 | namespace KoalaWiki.Domains; 5 | 6 | public class Document : Entity 7 | { 8 | /// 9 | /// 关联id 10 | /// 11 | public string WarehouseId { get; set; } = string.Empty; 12 | 13 | /// 14 | /// 最后更新时间 15 | /// 16 | public DateTime LastUpdate { get; set; } = DateTime.UtcNow; 17 | 18 | /// 19 | /// 文档介绍 20 | /// 21 | public string Description { get; set; } = string.Empty; 22 | 23 | /// 24 | /// 浏览量 25 | /// 26 | public long LikeCount { get; set; } = 0; 27 | 28 | /// 29 | /// 评论量 30 | /// 31 | public long CommentCount { get; set; } = 0; 32 | 33 | /// 34 | /// 本地git仓库地址 35 | /// 36 | /// 37 | public string GitPath { get; set; } = string.Empty; 38 | 39 | /// 40 | /// 仓库状态 41 | /// 42 | public WarehouseStatus Status { get; set; } 43 | } -------------------------------------------------------------------------------- /KoalaWiki.Domains/DocumentCatalog.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace KoalaWiki.Domains; 5 | 6 | public class DocumentCatalog : Entity 7 | { 8 | /// 9 | /// 目录名称 10 | /// 11 | public string Name { get; set; } = string.Empty; 12 | 13 | public string Url { get; set; } = string.Empty; 14 | 15 | /// 16 | /// 目录描述 17 | /// 18 | public string Description { get; set; } = string.Empty; 19 | 20 | /// 21 | /// 目录父级Id 22 | /// 23 | /// 24 | public string? ParentId { get; set; } = string.Empty; 25 | 26 | /// 27 | /// 当前目录排序 28 | /// 29 | public int Order { get; set; } = 0; 30 | 31 | /// 32 | /// 文档id 33 | /// 34 | public string DucumentId { get; set; } = string.Empty; 35 | 36 | public string WarehouseId { get; set; } = string.Empty; 37 | 38 | /// 39 | /// 是否处理完成 40 | /// 41 | public bool IsCompleted { get; set; } = false; 42 | 43 | public string Prompt { get; set; } = string.Empty; 44 | 45 | /// 46 | /// 是否删除 47 | /// 48 | public bool IsDeleted { get; set; } = false; 49 | 50 | public DateTime? DeletedTime { get; set; } = null; 51 | 52 | public List DependentFile { get; set; } = new(); 53 | } -------------------------------------------------------------------------------- /KoalaWiki.Domains/DocumentCommitRecord.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace KoalaWiki.Domains; 4 | 5 | public class DocumentCommitRecord : Entity 6 | { 7 | public string WarehouseId { get; set; } = string.Empty; 8 | 9 | public string CommitId { get; set; } = string.Empty; 10 | 11 | public string CommitMessage { get; set; } = string.Empty; 12 | 13 | public string Title { get; set; } = string.Empty; 14 | 15 | public string Author { get; set; } = string.Empty; 16 | 17 | public DateTime LastUpdate { get; set; } = DateTime.Now; 18 | } -------------------------------------------------------------------------------- /KoalaWiki.Domains/DocumentFile/DocumentFileItem.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.ComponentModel.DataAnnotations.Schema; 3 | using KoalaWiki.Domains; 4 | 5 | namespace KoalaWiki.Entities.DocumentFile; 6 | 7 | /// 8 | public class DocumentFileItem : Entity 9 | { 10 | /// 11 | /// 标题 12 | /// 13 | public string Title { get; set; } 14 | 15 | /// 16 | /// 描述 17 | /// 18 | public string Description { get; set; } 19 | 20 | /// 21 | /// 文档实际内容 22 | /// 23 | public string Content { get; set; } 24 | 25 | /// 26 | /// 评论数量 27 | /// 28 | public long CommentCount { get; set; } 29 | 30 | /// 31 | /// 文档大小 32 | /// 33 | public long Size { get; set; } 34 | 35 | /// 36 | /// 绑定的目录ID 37 | /// 38 | public string DocumentCatalogId { get; set; } 39 | 40 | /// 41 | /// 请求token消耗 42 | /// 43 | /// 44 | public int RequestToken { get; set; } 45 | 46 | /// 47 | /// 响应token 48 | /// 49 | public int ResponseToken { get; set; } 50 | 51 | /// 52 | /// 是否嵌入完成 53 | /// 54 | public bool IsEmbedded { get; set; } 55 | 56 | /// 57 | /// 相关源文件 58 | /// 59 | /// 60 | [NotMapped] 61 | public List? Source { get; set; } 62 | 63 | /// 64 | /// 源数据 65 | /// 66 | public Dictionary Metadata { get; set; } = new(); 67 | 68 | /// 69 | /// 扩展数据 70 | /// 71 | public Dictionary Extra { get; set; } = new(); 72 | } -------------------------------------------------------------------------------- /KoalaWiki.Domains/DocumentFile/DocumentFileItemSource.cs: -------------------------------------------------------------------------------- 1 | using KoalaWiki.Domains; 2 | 3 | namespace KoalaWiki.Entities.DocumentFile; 4 | 5 | public class DocumentFileItemSource : Entity 6 | { 7 | public string DocumentFileItemId { get; set; } = string.Empty; 8 | 9 | /// 10 | /// 源文件地址 11 | /// 12 | public string Address { get; set; } = string.Empty; 13 | 14 | /// 15 | /// 源文件名称 16 | /// 17 | public string? Name { get; set; } 18 | 19 | public DocumentFileItem DocumentFileItem { get; set; } = null!; 20 | } -------------------------------------------------------------------------------- /KoalaWiki.Domains/DocumentOverview.cs: -------------------------------------------------------------------------------- 1 | using KoalaWiki.Domains; 2 | 3 | namespace KoalaWiki.Entities; 4 | 5 | public class DocumentOverview : Entity 6 | { 7 | /// 8 | /// 绑定的ID 9 | /// 10 | public string DocumentId { get; set; } 11 | 12 | public string Content { get; set; } 13 | 14 | public string Title { get; set; } 15 | } -------------------------------------------------------------------------------- /KoalaWiki.Domains/Entity.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using KoalaWiki.Entities; 3 | 4 | namespace KoalaWiki.Domains; 5 | 6 | public class Entity : IEntity, ICreateEntity 7 | { 8 | public TKey Id { get; set; } 9 | 10 | public DateTime CreatedAt { get; set; } 11 | } -------------------------------------------------------------------------------- /KoalaWiki.Domains/FineTuning/FineTuningTask.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace KoalaWiki.Domains.FineTuning; 4 | 5 | public class FineTuningTask : Entity 6 | { 7 | public string WarehouseId { get; set; } = string.Empty; // 仓库ID 8 | 9 | /// 10 | /// 训练数据集 11 | /// 12 | public string TrainingDatasetId { get; set; } = string.Empty; 13 | 14 | /// 15 | /// 数据集目录ID 16 | /// 17 | public string DocumentCatalogId { get; set; } = string.Empty; 18 | 19 | public string Name { get; set; } 20 | 21 | public string UserId { get; set; } 22 | 23 | /// 24 | /// 任务描述 25 | /// 26 | public string Description { get; set; } 27 | 28 | /// 29 | /// 任务创建时间 30 | /// 31 | public DateTime CreatedAt { get; set; } 32 | 33 | /// 34 | /// 任务开始时间 35 | /// 36 | public DateTime? StartedAt { get; set; } 37 | 38 | /// 39 | /// 任务完成时间 40 | /// 41 | public DateTime? CompletedAt { get; set; } 42 | 43 | /// 44 | /// 任务进度 45 | /// 46 | public FineTuningTaskStatus Status { get; set; } = FineTuningTaskStatus.NotStarted; 47 | 48 | /// 49 | /// 数据集 50 | /// 51 | public string Dataset { get; set; } = string.Empty; 52 | 53 | /// 54 | /// Error 55 | /// 56 | public string? Error { get; set; } = string.Empty; 57 | 58 | /// 59 | /// 原始数据集 60 | /// 61 | public string? OriginalDataset { get; set; } = string.Empty; 62 | 63 | public DocumentCatalog DocumentCatalog { get; set; } 64 | } 65 | -------------------------------------------------------------------------------- /KoalaWiki.Domains/FineTuning/FineTuningTaskStatus.cs: -------------------------------------------------------------------------------- 1 | namespace KoalaWiki.Domains.FineTuning; 2 | 3 | public enum FineTuningTaskStatus 4 | { 5 | /// 6 | /// 未开始 7 | /// 8 | NotStarted = 0, 9 | 10 | /// 11 | /// 进行中 12 | /// 13 | InProgress = 1, 14 | 15 | /// 16 | /// 已完成 17 | /// 18 | Completed = 2, 19 | 20 | /// 21 | /// 失败 22 | /// 23 | Failed = 3, 24 | 25 | /// 26 | /// 已取消 27 | /// 28 | Cancelled = 4, 29 | } -------------------------------------------------------------------------------- /KoalaWiki.Domains/FineTuning/TrainingDataset.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace KoalaWiki.Domains.FineTuning; 4 | 5 | /// 6 | /// 训练数据集 7 | /// 8 | public class TrainingDataset : Entity 9 | { 10 | public string WarehouseId { get; set; } = string.Empty; // 仓库ID 11 | 12 | public string UserId { get; set; } 13 | 14 | /// 15 | /// 数据集生成时间 16 | /// 17 | public DateTime CreatedAt { get; set; } 18 | 19 | /// 20 | /// 数据集最后更新时间 21 | /// 22 | public DateTime UpdatedAt { get; set; } 23 | 24 | /// 25 | /// 数据集名称 26 | /// 27 | public TrainingDatasetStatus Status { get; set; } = TrainingDatasetStatus.NotStarted; 28 | 29 | public string Name { get; set; } = string.Empty; // 数据集名称 30 | 31 | /// 32 | /// API接口地址 33 | /// 34 | public string Endpoint { get; set; } = string.Empty; 35 | 36 | /// 37 | /// API密钥 38 | /// 39 | public string ApiKey { get; set; } = string.Empty; 40 | 41 | /// 42 | /// Prompt 43 | /// 44 | public string Prompt { get; set; } = string.Empty; 45 | 46 | /// 47 | /// 模型名称 48 | /// 49 | public string Model { get; set; } = string.Empty; // 模型名称 50 | } -------------------------------------------------------------------------------- /KoalaWiki.Domains/FineTuning/TrainingDatasetStatus.cs: -------------------------------------------------------------------------------- 1 | namespace KoalaWiki.Domains; 2 | 3 | public enum TrainingDatasetStatus 4 | { 5 | /// 6 | /// 未开始 7 | /// 8 | NotStarted = 0, 9 | 10 | /// 11 | /// 进行中 12 | /// 13 | InProgress = 1, 14 | 15 | /// 16 | /// 已完成 17 | /// 18 | Completed = 2, 19 | 20 | /// 21 | /// 失败 22 | /// 23 | Failed = 3, 24 | } -------------------------------------------------------------------------------- /KoalaWiki.Domains/ICreateEntity.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace KoalaWiki.Entities; 4 | 5 | public interface ICreateEntity 6 | { 7 | public DateTime CreatedAt { get; set; } 8 | } -------------------------------------------------------------------------------- /KoalaWiki.Domains/IEntity.cs: -------------------------------------------------------------------------------- 1 | namespace KoalaWiki.Domains; 2 | 3 | public interface IEntity 4 | { 5 | public TKey Id { get; set; } 6 | } -------------------------------------------------------------------------------- /KoalaWiki.Domains/KoalaWiki.Domains.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net9.0 5 | disable 6 | enable 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /KoalaWiki.Domains/MCP/MCPHistory.cs: -------------------------------------------------------------------------------- 1 | namespace KoalaWiki.Domains.MCP; 2 | 3 | public class MCPHistory : Entity 4 | { 5 | /// 6 | /// 提问内容 7 | /// 8 | public string Question { get; set; } = null!; 9 | 10 | /// 11 | /// AI回复 12 | /// 13 | public string Answer { get; set; } = null!; 14 | 15 | /// 16 | /// 用户id 17 | /// 18 | public string? UserId { get; set; } 19 | 20 | /// 21 | /// 仓库id 22 | /// 23 | public string? WarehouseId { get; set; } 24 | 25 | /// 26 | /// 耗时 27 | /// 28 | /// 29 | public int CostTime { get; set; } 30 | 31 | /// 32 | /// 请求id 33 | /// 34 | public string Ip { get; set; } 35 | 36 | /// 37 | /// 来源客户端 38 | /// 39 | public string UserAgent { get; set; } = null!; 40 | } -------------------------------------------------------------------------------- /KoalaWiki.Domains/Users/User.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace KoalaWiki.Domains.Users; 4 | 5 | public class User : Entity 6 | { 7 | /// 8 | /// 用户名称 9 | /// 10 | public string Name { get; set; } = string.Empty; 11 | 12 | /// 13 | /// 用户邮箱 14 | /// 15 | public string Email { get; set; } = string.Empty; 16 | 17 | /// 18 | /// 用户密码 19 | /// 20 | public string Password { get; set; } = string.Empty; 21 | 22 | /// 23 | /// 用户角色 24 | /// 25 | public string Role { get; set; } = "user"; 26 | 27 | /// 28 | /// 用户头像 29 | /// 30 | public string Avatar { get; set; } = string.Empty; 31 | 32 | /// 33 | /// 用户创建时间 34 | /// 35 | public DateTime CreatedAt { get; set; } = DateTime.UtcNow; 36 | 37 | /// 38 | /// 用户更新时间 39 | /// 40 | public DateTime? UpdatedAt { get; set; } 41 | 42 | /// 43 | /// 用户最后登录时间 44 | /// 45 | public DateTime? LastLoginAt { get; set; } 46 | 47 | /// 48 | /// 用户最后登录IP 49 | /// 50 | public string? LastLoginIp { get; set; } 51 | } -------------------------------------------------------------------------------- /KoalaWiki.Domains/Warehouse.cs: -------------------------------------------------------------------------------- 1 | using KoalaWiki.Domains; 2 | 3 | namespace KoalaWiki.Entities; 4 | 5 | public class Warehouse : Entity 6 | { 7 | /// 8 | /// 组织名称 9 | /// 10 | /// 11 | public string OrganizationName { get; set; } = string.Empty; 12 | 13 | /// 14 | /// 仓库名称 15 | /// 16 | public string Name { get; set; } 17 | 18 | /// 19 | /// 仓库描述 20 | /// 21 | public string Description { get; set; } 22 | 23 | /// 24 | /// 仓库地址 25 | /// 26 | /// 27 | public string Address { get; set; } 28 | 29 | /// 30 | /// 私有化git账号 31 | /// 32 | public string? GitUserName { get; set; } 33 | 34 | /// 35 | /// 私有化git密码 36 | /// 37 | public string? GitPassword { get; set; } 38 | 39 | /// 40 | /// 私有化git邮箱 41 | /// 42 | public string? Email { get; set; } 43 | 44 | /// 45 | /// 仓库类型 46 | /// 47 | public string? Type { get; set; } 48 | 49 | /// 50 | /// 仓库分支 51 | /// 52 | public string? Branch { get; set; } 53 | 54 | /// 55 | /// 仓库状态 56 | /// 57 | public WarehouseStatus Status { get; set; } 58 | 59 | /// 60 | /// 错误信息 61 | /// 62 | public string? Error { get; set; } 63 | 64 | /// 65 | /// 构建提示词 66 | /// 67 | public string? Prompt { get; set; } 68 | 69 | /// 70 | /// 仓库版本 71 | /// 72 | public string? Version { get; set; } 73 | 74 | /// 75 | /// 是否嵌入完成 76 | /// 77 | public bool IsEmbedded { get; set; } 78 | 79 | /// 80 | /// 是否推荐 81 | /// 82 | /// 83 | public bool IsRecommended { get; set; } 84 | 85 | /// 86 | /// 优化过的代码目录结构 87 | /// 88 | /// 89 | public string? OptimizedDirectoryStructure { get; set; } 90 | 91 | /// 92 | /// 当前仓库的文档(默认使用仓库,如果没有则动态生成) 93 | /// 94 | public string? Readme { get; set; } 95 | 96 | } -------------------------------------------------------------------------------- /KoalaWiki.Domains/WarehouseStatus.cs: -------------------------------------------------------------------------------- 1 | namespace KoalaWiki.Entities; 2 | 3 | public enum WarehouseStatus : byte 4 | { 5 | /// 6 | /// 待处理 7 | /// 8 | Pending = 0, 9 | 10 | /// 11 | /// 处理中 12 | /// 13 | Processing = 1, 14 | 15 | /// 16 | /// 已完成 17 | /// 18 | Completed = 2, 19 | 20 | /// 21 | /// 已取消 22 | /// 23 | Canceled = 3, 24 | 25 | /// 26 | /// 未授权 27 | /// 28 | Unauthorized = 4, 29 | 30 | /// 31 | /// 已失败 32 | //// 33 | Failed = 99 34 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 AIDotNet 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /NuGet.Config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /Provider/KoalaWiki.Provider.PostgreSQL/KoalaWiki.Provider.PostgreSQL.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net9.0 5 | latest 6 | enable 7 | enable 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /Provider/KoalaWiki.Provider.PostgreSQL/Migrations/20250430080021_AddDocumentCommitRecords.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.EntityFrameworkCore.Migrations; 3 | 4 | #nullable disable 5 | 6 | namespace KoalaWiki.Provider.PostgreSQL.Migrations 7 | { 8 | /// 9 | public partial class AddDocumentCommitRecords : Migration 10 | { 11 | /// 12 | protected override void Up(MigrationBuilder migrationBuilder) 13 | { 14 | migrationBuilder.CreateTable( 15 | name: "DocumentCommitRecords", 16 | columns: table => new 17 | { 18 | Id = table.Column(type: "text", nullable: false), 19 | WarehouseId = table.Column(type: "text", nullable: false), 20 | CommitId = table.Column(type: "text", nullable: false), 21 | CommitMessage = table.Column(type: "text", nullable: false), 22 | Author = table.Column(type: "text", nullable: false), 23 | LastUpdate = table.Column(type: "timestamp without time zone", nullable: false), 24 | CreatedAt = table.Column(type: "timestamp without time zone", nullable: false) 25 | }, 26 | constraints: table => 27 | { 28 | table.PrimaryKey("PK_DocumentCommitRecords", x => x.Id); 29 | }); 30 | 31 | migrationBuilder.CreateIndex( 32 | name: "IX_DocumentCommitRecords_CommitId", 33 | table: "DocumentCommitRecords", 34 | column: "CommitId"); 35 | 36 | migrationBuilder.CreateIndex( 37 | name: "IX_DocumentCommitRecords_WarehouseId", 38 | table: "DocumentCommitRecords", 39 | column: "WarehouseId"); 40 | } 41 | 42 | /// 43 | protected override void Down(MigrationBuilder migrationBuilder) 44 | { 45 | migrationBuilder.DropTable( 46 | name: "DocumentCommitRecords"); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Provider/KoalaWiki.Provider.PostgreSQL/Migrations/20250430094304_AddGit.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.Migrations; 2 | 3 | #nullable disable 4 | 5 | namespace KoalaWiki.Provider.PostgreSQL.Migrations 6 | { 7 | /// 8 | public partial class AddGit : Migration 9 | { 10 | /// 11 | protected override void Up(MigrationBuilder migrationBuilder) 12 | { 13 | migrationBuilder.AddColumn( 14 | name: "GitPassword", 15 | table: "Warehouses", 16 | type: "text", 17 | nullable: true); 18 | 19 | migrationBuilder.AddColumn( 20 | name: "GitUserName", 21 | table: "Warehouses", 22 | type: "text", 23 | nullable: true); 24 | } 25 | 26 | /// 27 | protected override void Down(MigrationBuilder migrationBuilder) 28 | { 29 | migrationBuilder.DropColumn( 30 | name: "GitPassword", 31 | table: "Warehouses"); 32 | 33 | migrationBuilder.DropColumn( 34 | name: "GitUserName", 35 | table: "Warehouses"); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Provider/KoalaWiki.Provider.PostgreSQL/Migrations/20250430095405_AddEmail.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.Migrations; 2 | 3 | #nullable disable 4 | 5 | namespace KoalaWiki.Provider.PostgreSQL.Migrations 6 | { 7 | /// 8 | public partial class AddEmail : Migration 9 | { 10 | /// 11 | protected override void Up(MigrationBuilder migrationBuilder) 12 | { 13 | migrationBuilder.AddColumn( 14 | name: "Email", 15 | table: "Warehouses", 16 | type: "text", 17 | nullable: true); 18 | } 19 | 20 | /// 21 | protected override void Down(MigrationBuilder migrationBuilder) 22 | { 23 | migrationBuilder.DropColumn( 24 | name: "Email", 25 | table: "Warehouses"); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Provider/KoalaWiki.Provider.PostgreSQL/Migrations/20250506101108_AddEmbedded.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.Migrations; 2 | 3 | #nullable disable 4 | 5 | namespace KoalaWiki.Provider.PostgreSQL.Migrations 6 | { 7 | /// 8 | public partial class AddEmbedded : Migration 9 | { 10 | /// 11 | protected override void Up(MigrationBuilder migrationBuilder) 12 | { 13 | migrationBuilder.DropColumn( 14 | name: "Model", 15 | table: "Warehouses"); 16 | 17 | migrationBuilder.DropColumn( 18 | name: "OpenAIEndpoint", 19 | table: "Warehouses"); 20 | 21 | migrationBuilder.DropColumn( 22 | name: "OpenAIKey", 23 | table: "Warehouses"); 24 | 25 | migrationBuilder.AddColumn( 26 | name: "IsEmbedded", 27 | table: "Warehouses", 28 | type: "boolean", 29 | nullable: false, 30 | defaultValue: false); 31 | 32 | migrationBuilder.AddColumn( 33 | name: "IsEmbedded", 34 | table: "DocumentFileItems", 35 | type: "boolean", 36 | nullable: false, 37 | defaultValue: false); 38 | } 39 | 40 | /// 41 | protected override void Down(MigrationBuilder migrationBuilder) 42 | { 43 | migrationBuilder.DropColumn( 44 | name: "IsEmbedded", 45 | table: "Warehouses"); 46 | 47 | migrationBuilder.DropColumn( 48 | name: "IsEmbedded", 49 | table: "DocumentFileItems"); 50 | 51 | migrationBuilder.AddColumn( 52 | name: "Model", 53 | table: "Warehouses", 54 | type: "text", 55 | nullable: false, 56 | defaultValue: ""); 57 | 58 | migrationBuilder.AddColumn( 59 | name: "OpenAIEndpoint", 60 | table: "Warehouses", 61 | type: "text", 62 | nullable: false, 63 | defaultValue: ""); 64 | 65 | migrationBuilder.AddColumn( 66 | name: "OpenAIKey", 67 | table: "Warehouses", 68 | type: "text", 69 | nullable: false, 70 | defaultValue: ""); 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /Provider/KoalaWiki.Provider.PostgreSQL/Migrations/20250506195024_AddQuestion.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.Migrations; 2 | 3 | #nullable disable 4 | 5 | namespace KoalaWiki.Provider.PostgreSQL.Migrations 6 | { 7 | /// 8 | public partial class AddQuestion : Migration 9 | { 10 | /// 11 | protected override void Up(MigrationBuilder migrationBuilder) 12 | { 13 | migrationBuilder.AddColumn( 14 | name: "Question", 15 | table: "ChatShareMessages", 16 | type: "text", 17 | nullable: false, 18 | defaultValue: ""); 19 | } 20 | 21 | /// 22 | protected override void Down(MigrationBuilder migrationBuilder) 23 | { 24 | migrationBuilder.DropColumn( 25 | name: "Question", 26 | table: "ChatShareMessages"); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Provider/KoalaWiki.Provider.PostgreSQL/Migrations/20250507090410_AddFile.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.Migrations; 2 | 3 | #nullable disable 4 | 5 | namespace KoalaWiki.Provider.PostgreSQL.Migrations 6 | { 7 | /// 8 | public partial class AddFile : Migration 9 | { 10 | /// 11 | protected override void Up(MigrationBuilder migrationBuilder) 12 | { 13 | migrationBuilder.AddColumn( 14 | name: "Files", 15 | table: "ChatShareMessageItems", 16 | type: "text", 17 | nullable: false, 18 | defaultValue: ""); 19 | 20 | migrationBuilder.CreateIndex( 21 | name: "IX_ChatShareMessageItems_WarehouseId", 22 | table: "ChatShareMessageItems", 23 | column: "WarehouseId"); 24 | } 25 | 26 | /// 27 | protected override void Down(MigrationBuilder migrationBuilder) 28 | { 29 | migrationBuilder.DropIndex( 30 | name: "IX_ChatShareMessageItems_WarehouseId", 31 | table: "ChatShareMessageItems"); 32 | 33 | migrationBuilder.DropColumn( 34 | name: "Files", 35 | table: "ChatShareMessageItems"); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Provider/KoalaWiki.Provider.PostgreSQL/Migrations/20250508100054_AddRecommended.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.Migrations; 2 | 3 | #nullable disable 4 | 5 | namespace KoalaWiki.Provider.PostgreSQL.Migrations 6 | { 7 | /// 8 | public partial class AddRecommended : Migration 9 | { 10 | /// 11 | protected override void Up(MigrationBuilder migrationBuilder) 12 | { 13 | migrationBuilder.AddColumn( 14 | name: "IsRecommended", 15 | table: "Warehouses", 16 | type: "boolean", 17 | nullable: false, 18 | defaultValue: false); 19 | } 20 | 21 | /// 22 | protected override void Down(MigrationBuilder migrationBuilder) 23 | { 24 | migrationBuilder.DropColumn( 25 | name: "IsRecommended", 26 | table: "Warehouses"); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Provider/KoalaWiki.Provider.PostgreSQL/Migrations/20250509063050_AddIsCompleted.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.Migrations; 2 | 3 | #nullable disable 4 | 5 | namespace KoalaWiki.Provider.PostgreSQL.Migrations 6 | { 7 | /// 8 | public partial class AddIsCompleted : Migration 9 | { 10 | /// 11 | protected override void Up(MigrationBuilder migrationBuilder) 12 | { 13 | migrationBuilder.AddColumn( 14 | name: "IsCompleted", 15 | table: "DocumentCatalogs", 16 | type: "boolean", 17 | nullable: false, 18 | defaultValue: false); 19 | } 20 | 21 | /// 22 | protected override void Down(MigrationBuilder migrationBuilder) 23 | { 24 | migrationBuilder.DropColumn( 25 | name: "IsCompleted", 26 | table: "DocumentCatalogs"); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Provider/KoalaWiki.Provider.PostgreSQL/Migrations/20250510081053_AddFiles.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.Migrations; 2 | 3 | #nullable disable 4 | 5 | namespace KoalaWiki.Provider.PostgreSQL.Migrations 6 | { 7 | /// 8 | public partial class AddFiles : Migration 9 | { 10 | /// 11 | protected override void Up(MigrationBuilder migrationBuilder) 12 | { 13 | migrationBuilder.AddColumn( 14 | name: "OptimizedDirectoryStructure", 15 | table: "Warehouses", 16 | type: "text", 17 | nullable: false, 18 | defaultValue: ""); 19 | 20 | migrationBuilder.AddColumn( 21 | name: "DependentFile", 22 | table: "DocumentCatalogs", 23 | type: "text", 24 | nullable: false, 25 | defaultValue: ""); 26 | 27 | migrationBuilder.AddColumn( 28 | name: "Prompt", 29 | table: "DocumentCatalogs", 30 | type: "text", 31 | nullable: false, 32 | defaultValue: ""); 33 | } 34 | 35 | /// 36 | protected override void Down(MigrationBuilder migrationBuilder) 37 | { 38 | migrationBuilder.DropColumn( 39 | name: "OptimizedDirectoryStructure", 40 | table: "Warehouses"); 41 | 42 | migrationBuilder.DropColumn( 43 | name: "DependentFile", 44 | table: "DocumentCatalogs"); 45 | 46 | migrationBuilder.DropColumn( 47 | name: "Prompt", 48 | table: "DocumentCatalogs"); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Provider/KoalaWiki.Provider.PostgreSQL/Migrations/20250512065940_AddDeletedTime.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.EntityFrameworkCore.Migrations; 3 | 4 | #nullable disable 5 | 6 | namespace KoalaWiki.Provider.PostgreSQL.Migrations 7 | { 8 | /// 9 | public partial class AddDeletedTime : Migration 10 | { 11 | /// 12 | protected override void Up(MigrationBuilder migrationBuilder) 13 | { 14 | migrationBuilder.AddColumn( 15 | name: "DeletedTime", 16 | table: "DocumentCatalogs", 17 | type: "timestamp without time zone", 18 | nullable: true); 19 | 20 | migrationBuilder.AddColumn( 21 | name: "IsDeleted", 22 | table: "DocumentCatalogs", 23 | type: "boolean", 24 | nullable: false, 25 | defaultValue: false); 26 | 27 | migrationBuilder.CreateIndex( 28 | name: "IX_DocumentCatalogs_IsDeleted", 29 | table: "DocumentCatalogs", 30 | column: "IsDeleted"); 31 | } 32 | 33 | /// 34 | protected override void Down(MigrationBuilder migrationBuilder) 35 | { 36 | migrationBuilder.DropIndex( 37 | name: "IX_DocumentCatalogs_IsDeleted", 38 | table: "DocumentCatalogs"); 39 | 40 | migrationBuilder.DropColumn( 41 | name: "DeletedTime", 42 | table: "DocumentCatalogs"); 43 | 44 | migrationBuilder.DropColumn( 45 | name: "IsDeleted", 46 | table: "DocumentCatalogs"); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Provider/KoalaWiki.Provider.PostgreSQL/Migrations/20250512154412_AddCommitTitle.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.Migrations; 2 | 3 | #nullable disable 4 | 5 | namespace KoalaWiki.Provider.PostgreSQL.Migrations 6 | { 7 | /// 8 | public partial class AddCommitTitle : Migration 9 | { 10 | /// 11 | protected override void Up(MigrationBuilder migrationBuilder) 12 | { 13 | migrationBuilder.AddColumn( 14 | name: "Title", 15 | table: "DocumentCommitRecords", 16 | type: "text", 17 | nullable: false, 18 | defaultValue: ""); 19 | } 20 | 21 | /// 22 | protected override void Down(MigrationBuilder migrationBuilder) 23 | { 24 | migrationBuilder.DropColumn( 25 | name: "Title", 26 | table: "DocumentCommitRecords"); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Provider/KoalaWiki.Provider.PostgreSQL/Migrations/20250515170036_AddUser.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.EntityFrameworkCore.Migrations; 3 | 4 | #nullable disable 5 | 6 | namespace KoalaWiki.Provider.PostgreSQL.Migrations 7 | { 8 | /// 9 | public partial class AddUser : Migration 10 | { 11 | /// 12 | protected override void Up(MigrationBuilder migrationBuilder) 13 | { 14 | migrationBuilder.CreateTable( 15 | name: "Users", 16 | columns: table => new 17 | { 18 | Id = table.Column(type: "text", nullable: false), 19 | Name = table.Column(type: "text", nullable: false), 20 | Email = table.Column(type: "text", nullable: false), 21 | Password = table.Column(type: "text", nullable: false), 22 | Role = table.Column(type: "text", nullable: false), 23 | Avatar = table.Column(type: "text", nullable: false), 24 | CreatedAt = table.Column(type: "timestamp without time zone", nullable: false), 25 | UpdatedAt = table.Column(type: "timestamp without time zone", nullable: true), 26 | LastLoginAt = table.Column(type: "timestamp without time zone", nullable: true), 27 | LastLoginIp = table.Column(type: "text", nullable: true) 28 | }, 29 | constraints: table => 30 | { 31 | table.PrimaryKey("PK_Users", x => x.Id); 32 | }); 33 | 34 | migrationBuilder.CreateIndex( 35 | name: "IX_Users_CreatedAt", 36 | table: "Users", 37 | column: "CreatedAt"); 38 | 39 | migrationBuilder.CreateIndex( 40 | name: "IX_Users_Email", 41 | table: "Users", 42 | column: "Email"); 43 | 44 | migrationBuilder.CreateIndex( 45 | name: "IX_Users_LastLoginAt", 46 | table: "Users", 47 | column: "LastLoginAt"); 48 | 49 | migrationBuilder.CreateIndex( 50 | name: "IX_Users_Name", 51 | table: "Users", 52 | column: "Name"); 53 | } 54 | 55 | /// 56 | protected override void Down(MigrationBuilder migrationBuilder) 57 | { 58 | migrationBuilder.DropTable( 59 | name: "Users"); 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /Provider/KoalaWiki.Provider.PostgreSQL/Migrations/20250523102229_AddMCPHistory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.EntityFrameworkCore.Migrations; 3 | 4 | #nullable disable 5 | 6 | namespace KoalaWiki.Provider.PostgreSQL.Migrations 7 | { 8 | /// 9 | public partial class AddMCPHistory : Migration 10 | { 11 | /// 12 | protected override void Up(MigrationBuilder migrationBuilder) 13 | { 14 | migrationBuilder.CreateTable( 15 | name: "MCPHistories", 16 | columns: table => new 17 | { 18 | Id = table.Column(type: "text", nullable: false, comment: "主键Id"), 19 | Question = table.Column(type: "text", nullable: false), 20 | Answer = table.Column(type: "text", nullable: false), 21 | UserId = table.Column(type: "text", nullable: true), 22 | WarehouseId = table.Column(type: "text", nullable: true), 23 | CostTime = table.Column(type: "integer", nullable: false), 24 | Ip = table.Column(type: "text", nullable: false), 25 | UserAgent = table.Column(type: "text", nullable: false), 26 | CreatedAt = table.Column(type: "timestamp without time zone", nullable: false) 27 | }, 28 | constraints: table => 29 | { 30 | table.PrimaryKey("PK_MCPHistories", x => x.Id); 31 | }, 32 | comment: "MCP历史记录"); 33 | 34 | migrationBuilder.CreateIndex( 35 | name: "IX_MCPHistories_CreatedAt", 36 | table: "MCPHistories", 37 | column: "CreatedAt"); 38 | 39 | migrationBuilder.CreateIndex( 40 | name: "IX_MCPHistories_UserId", 41 | table: "MCPHistories", 42 | column: "UserId"); 43 | 44 | migrationBuilder.CreateIndex( 45 | name: "IX_MCPHistories_WarehouseId", 46 | table: "MCPHistories", 47 | column: "WarehouseId"); 48 | } 49 | 50 | /// 51 | protected override void Down(MigrationBuilder migrationBuilder) 52 | { 53 | migrationBuilder.DropTable( 54 | name: "MCPHistories"); 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /Provider/KoalaWiki.Provider.PostgreSQL/PostgreSQLApplicationExtensions.cs: -------------------------------------------------------------------------------- 1 | using KoalaWiki.Core; 2 | using Microsoft.EntityFrameworkCore; 3 | using Microsoft.Extensions.Configuration; 4 | using Microsoft.Extensions.DependencyInjection; 5 | using Microsoft.Extensions.Logging; 6 | 7 | namespace KoalaWiki.Provider.PostgreSQL; 8 | 9 | 10 | public static class PostgreSQLApplicationExtensions 11 | { 12 | public static IServiceCollection AddPostgreSQLDbContext(this IServiceCollection services, 13 | IConfiguration configuration) 14 | { 15 | 16 | services.AddDataAccess((_, builder) => 17 | { 18 | builder.UseNpgsql(configuration.GetConnectionString("Default")); 19 | 20 | // sql日志不输出控制台 21 | builder.UseLoggerFactory(LoggerFactory.Create(_ => { })); 22 | }); 23 | 24 | 25 | return services; 26 | } 27 | 28 | public static IServiceCollection AddPostgreSQLDbContext(this IServiceCollection services, 29 | string connectionString) 30 | { 31 | 32 | services.AddDataAccess(((provider, builder) => 33 | { 34 | builder.UseNpgsql(connectionString); 35 | 36 | // sql日志不输出控制台 37 | builder.UseLoggerFactory(LoggerFactory.Create(_ => { })); 38 | })); 39 | 40 | 41 | return services; 42 | } 43 | } -------------------------------------------------------------------------------- /Provider/KoalaWiki.Provider.PostgreSQL/PostgreSQLContext.cs: -------------------------------------------------------------------------------- 1 | using KoalaWiki.Core.DataAccess; 2 | using Microsoft.EntityFrameworkCore; 3 | using Microsoft.EntityFrameworkCore.Diagnostics; 4 | 5 | namespace KoalaWiki.Provider.PostgreSQL; 6 | 7 | public class PostgreSQLContext(DbContextOptions options) 8 | : KoalaWikiContext(options) 9 | { 10 | protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) 11 | { 12 | optionsBuilder.ConfigureWarnings(warnings => 13 | warnings.Ignore(RelationalEventId.PendingModelChangesWarning)); 14 | } 15 | } -------------------------------------------------------------------------------- /Provider/KoalaWiki.Provider.SqlServer/KoalaWiki.Provider.SqlServer.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net9.0 5 | enable 6 | enable 7 | 8 | 9 | 10 | 11 | 12 | all 13 | runtime; build; native; contentfiles; analyzers; buildtransitive 14 | 15 | 16 | all 17 | runtime; build; native; contentfiles; analyzers; buildtransitive 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /Provider/KoalaWiki.Provider.SqlServer/Migrations/20250515170141_AddUser.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.EntityFrameworkCore.Migrations; 3 | 4 | #nullable disable 5 | 6 | namespace KoalaWiki.Provider.SqlServer.Migrations 7 | { 8 | /// 9 | public partial class AddUser : Migration 10 | { 11 | /// 12 | protected override void Up(MigrationBuilder migrationBuilder) 13 | { 14 | migrationBuilder.CreateTable( 15 | name: "Users", 16 | columns: table => new 17 | { 18 | Id = table.Column(type: "nvarchar(450)", nullable: false), 19 | Name = table.Column(type: "nvarchar(450)", nullable: false), 20 | Email = table.Column(type: "nvarchar(450)", nullable: false), 21 | Password = table.Column(type: "nvarchar(max)", nullable: false), 22 | Role = table.Column(type: "nvarchar(max)", nullable: false), 23 | Avatar = table.Column(type: "nvarchar(max)", nullable: false), 24 | CreatedAt = table.Column(type: "datetime2", nullable: false), 25 | UpdatedAt = table.Column(type: "datetime2", nullable: true), 26 | LastLoginAt = table.Column(type: "datetime2", nullable: true), 27 | LastLoginIp = table.Column(type: "nvarchar(max)", nullable: true) 28 | }, 29 | constraints: table => 30 | { 31 | table.PrimaryKey("PK_Users", x => x.Id); 32 | }); 33 | 34 | migrationBuilder.CreateIndex( 35 | name: "IX_Users_CreatedAt", 36 | table: "Users", 37 | column: "CreatedAt"); 38 | 39 | migrationBuilder.CreateIndex( 40 | name: "IX_Users_Email", 41 | table: "Users", 42 | column: "Email"); 43 | 44 | migrationBuilder.CreateIndex( 45 | name: "IX_Users_LastLoginAt", 46 | table: "Users", 47 | column: "LastLoginAt"); 48 | 49 | migrationBuilder.CreateIndex( 50 | name: "IX_Users_Name", 51 | table: "Users", 52 | column: "Name"); 53 | } 54 | 55 | /// 56 | protected override void Down(MigrationBuilder migrationBuilder) 57 | { 58 | migrationBuilder.DropTable( 59 | name: "Users"); 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /Provider/KoalaWiki.Provider.SqlServer/Migrations/20250523102304_AddMCPHistory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.EntityFrameworkCore.Migrations; 3 | 4 | #nullable disable 5 | 6 | namespace KoalaWiki.Provider.SqlServer.Migrations 7 | { 8 | /// 9 | public partial class AddMCPHistory : Migration 10 | { 11 | /// 12 | protected override void Up(MigrationBuilder migrationBuilder) 13 | { 14 | migrationBuilder.CreateTable( 15 | name: "MCPHistories", 16 | columns: table => new 17 | { 18 | Id = table.Column(type: "nvarchar(450)", nullable: false, comment: "主键Id"), 19 | Question = table.Column(type: "nvarchar(max)", nullable: false), 20 | Answer = table.Column(type: "nvarchar(max)", nullable: false), 21 | UserId = table.Column(type: "nvarchar(450)", nullable: true), 22 | WarehouseId = table.Column(type: "nvarchar(450)", nullable: true), 23 | CostTime = table.Column(type: "int", nullable: false), 24 | Ip = table.Column(type: "nvarchar(max)", nullable: false), 25 | UserAgent = table.Column(type: "nvarchar(max)", nullable: false), 26 | CreatedAt = table.Column(type: "datetime2", nullable: false) 27 | }, 28 | constraints: table => 29 | { 30 | table.PrimaryKey("PK_MCPHistories", x => x.Id); 31 | }, 32 | comment: "MCP历史记录"); 33 | 34 | migrationBuilder.CreateIndex( 35 | name: "IX_MCPHistories_CreatedAt", 36 | table: "MCPHistories", 37 | column: "CreatedAt"); 38 | 39 | migrationBuilder.CreateIndex( 40 | name: "IX_MCPHistories_UserId", 41 | table: "MCPHistories", 42 | column: "UserId"); 43 | 44 | migrationBuilder.CreateIndex( 45 | name: "IX_MCPHistories_WarehouseId", 46 | table: "MCPHistories", 47 | column: "WarehouseId"); 48 | } 49 | 50 | /// 51 | protected override void Down(MigrationBuilder migrationBuilder) 52 | { 53 | migrationBuilder.DropTable( 54 | name: "MCPHistories"); 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /Provider/KoalaWiki.Provider.SqlServer/SqlServerApplicationExtensions.cs: -------------------------------------------------------------------------------- 1 | using KoalaWiki.Core; 2 | using Microsoft.EntityFrameworkCore; 3 | using Microsoft.Extensions.Configuration; 4 | using Microsoft.Extensions.DependencyInjection; 5 | using Microsoft.Extensions.Logging; 6 | 7 | namespace KoalaWiki.Provider.SqlServer; 8 | 9 | public static class SqlServerApplicationExtensions 10 | { 11 | public static IServiceCollection AddSqlServerDbContext(this IServiceCollection services, 12 | IConfiguration configuration) 13 | { 14 | services.AddDataAccess((_, builder) => 15 | { 16 | builder.UseSqlServer(configuration.GetConnectionString("Default")); 17 | 18 | // sql日志不输出控制台 19 | builder.UseLoggerFactory(LoggerFactory.Create(_ => { })); 20 | }); 21 | 22 | return services; 23 | } 24 | 25 | public static IServiceCollection AddSqlServerDbContext(this IServiceCollection services, 26 | string connectionString) 27 | { 28 | services.AddDataAccess(((provider, builder) => 29 | { 30 | builder.UseSqlServer(connectionString); 31 | 32 | // sql日志不输出控制台 33 | builder.UseLoggerFactory(LoggerFactory.Create(_ => { })); 34 | })); 35 | 36 | return services; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Provider/KoalaWiki.Provider.SqlServer/SqlServerContext.cs: -------------------------------------------------------------------------------- 1 | using KoalaWiki.Core.DataAccess; 2 | using Microsoft.EntityFrameworkCore; 3 | using Microsoft.EntityFrameworkCore.Diagnostics; 4 | 5 | namespace KoalaWiki.Provider.SqlServer; 6 | 7 | public class SqlServerContext(DbContextOptions options) 8 | : KoalaWikiContext(options) 9 | { 10 | protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) 11 | { 12 | optionsBuilder.ConfigureWarnings(warnings => 13 | warnings.Ignore(RelationalEventId.PendingModelChangesWarning)); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Provider/KoalaWiki.Provider.Sqlite/KoalaWiki.Provider.Sqlite.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net9.0 5 | enable 6 | enable 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /Provider/KoalaWiki.Provider.Sqlite/Migrations/20250430075934_AddDocumentCommitRecords.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.EntityFrameworkCore.Migrations; 3 | 4 | #nullable disable 5 | 6 | namespace KoalaWiki.Provider.Sqlite.Migrations 7 | { 8 | /// 9 | public partial class AddDocumentCommitRecords : Migration 10 | { 11 | /// 12 | protected override void Up(MigrationBuilder migrationBuilder) 13 | { 14 | migrationBuilder.CreateTable( 15 | name: "DocumentCommitRecords", 16 | columns: table => new 17 | { 18 | Id = table.Column(type: "TEXT", nullable: false), 19 | WarehouseId = table.Column(type: "TEXT", nullable: false), 20 | CommitId = table.Column(type: "TEXT", nullable: false), 21 | CommitMessage = table.Column(type: "TEXT", nullable: false), 22 | Author = table.Column(type: "TEXT", nullable: false), 23 | LastUpdate = table.Column(type: "TEXT", nullable: false), 24 | CreatedAt = table.Column(type: "TEXT", nullable: false) 25 | }, 26 | constraints: table => 27 | { 28 | table.PrimaryKey("PK_DocumentCommitRecords", x => x.Id); 29 | }); 30 | 31 | migrationBuilder.CreateIndex( 32 | name: "IX_DocumentCommitRecords_CommitId", 33 | table: "DocumentCommitRecords", 34 | column: "CommitId"); 35 | 36 | migrationBuilder.CreateIndex( 37 | name: "IX_DocumentCommitRecords_WarehouseId", 38 | table: "DocumentCommitRecords", 39 | column: "WarehouseId"); 40 | } 41 | 42 | /// 43 | protected override void Down(MigrationBuilder migrationBuilder) 44 | { 45 | migrationBuilder.DropTable( 46 | name: "DocumentCommitRecords"); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Provider/KoalaWiki.Provider.Sqlite/Migrations/20250430094200_AddGitUser.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.Migrations; 2 | 3 | #nullable disable 4 | 5 | namespace KoalaWiki.Provider.Sqlite.Migrations 6 | { 7 | /// 8 | public partial class AddGitUser : Migration 9 | { 10 | /// 11 | protected override void Up(MigrationBuilder migrationBuilder) 12 | { 13 | migrationBuilder.AddColumn( 14 | name: "GitPassword", 15 | table: "Warehouses", 16 | type: "TEXT", 17 | nullable: true); 18 | 19 | migrationBuilder.AddColumn( 20 | name: "GitUserName", 21 | table: "Warehouses", 22 | type: "TEXT", 23 | nullable: true); 24 | } 25 | 26 | /// 27 | protected override void Down(MigrationBuilder migrationBuilder) 28 | { 29 | migrationBuilder.DropColumn( 30 | name: "GitPassword", 31 | table: "Warehouses"); 32 | 33 | migrationBuilder.DropColumn( 34 | name: "GitUserName", 35 | table: "Warehouses"); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Provider/KoalaWiki.Provider.Sqlite/Migrations/20250430095428_AddEmail.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.Migrations; 2 | 3 | #nullable disable 4 | 5 | namespace KoalaWiki.Provider.Sqlite.Migrations 6 | { 7 | /// 8 | public partial class AddEmail : Migration 9 | { 10 | /// 11 | protected override void Up(MigrationBuilder migrationBuilder) 12 | { 13 | migrationBuilder.AddColumn( 14 | name: "Email", 15 | table: "Warehouses", 16 | type: "TEXT", 17 | nullable: true); 18 | } 19 | 20 | /// 21 | protected override void Down(MigrationBuilder migrationBuilder) 22 | { 23 | migrationBuilder.DropColumn( 24 | name: "Email", 25 | table: "Warehouses"); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Provider/KoalaWiki.Provider.Sqlite/Migrations/20250506095725_AddEmbedded.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.Migrations; 2 | 3 | #nullable disable 4 | 5 | namespace KoalaWiki.Provider.Sqlite.Migrations 6 | { 7 | /// 8 | public partial class AddEmbedded : Migration 9 | { 10 | /// 11 | protected override void Up(MigrationBuilder migrationBuilder) 12 | { 13 | migrationBuilder.DropColumn( 14 | name: "Model", 15 | table: "Warehouses"); 16 | 17 | migrationBuilder.DropColumn( 18 | name: "OpenAIEndpoint", 19 | table: "Warehouses"); 20 | 21 | migrationBuilder.DropColumn( 22 | name: "OpenAIKey", 23 | table: "Warehouses"); 24 | 25 | migrationBuilder.AddColumn( 26 | name: "IsEmbedded", 27 | table: "Warehouses", 28 | type: "INTEGER", 29 | nullable: false, 30 | defaultValue: false); 31 | 32 | migrationBuilder.AddColumn( 33 | name: "IsEmbedded", 34 | table: "DocumentFileItems", 35 | type: "INTEGER", 36 | nullable: false, 37 | defaultValue: false); 38 | } 39 | 40 | /// 41 | protected override void Down(MigrationBuilder migrationBuilder) 42 | { 43 | migrationBuilder.DropColumn( 44 | name: "IsEmbedded", 45 | table: "Warehouses"); 46 | 47 | migrationBuilder.DropColumn( 48 | name: "IsEmbedded", 49 | table: "DocumentFileItems"); 50 | 51 | migrationBuilder.AddColumn( 52 | name: "Model", 53 | table: "Warehouses", 54 | type: "TEXT", 55 | nullable: false, 56 | defaultValue: ""); 57 | 58 | migrationBuilder.AddColumn( 59 | name: "OpenAIEndpoint", 60 | table: "Warehouses", 61 | type: "TEXT", 62 | nullable: false, 63 | defaultValue: ""); 64 | 65 | migrationBuilder.AddColumn( 66 | name: "OpenAIKey", 67 | table: "Warehouses", 68 | type: "TEXT", 69 | nullable: false, 70 | defaultValue: ""); 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /Provider/KoalaWiki.Provider.Sqlite/Migrations/20250506194943_AddQuestion.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.Migrations; 2 | 3 | #nullable disable 4 | 5 | namespace KoalaWiki.Provider.Sqlite.Migrations 6 | { 7 | /// 8 | public partial class AddQuestion : Migration 9 | { 10 | /// 11 | protected override void Up(MigrationBuilder migrationBuilder) 12 | { 13 | migrationBuilder.AddColumn( 14 | name: "Question", 15 | table: "ChatShareMessages", 16 | type: "TEXT", 17 | nullable: false, 18 | defaultValue: ""); 19 | } 20 | 21 | /// 22 | protected override void Down(MigrationBuilder migrationBuilder) 23 | { 24 | migrationBuilder.DropColumn( 25 | name: "Question", 26 | table: "ChatShareMessages"); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Provider/KoalaWiki.Provider.Sqlite/Migrations/20250507090336_AddFile.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.Migrations; 2 | 3 | #nullable disable 4 | 5 | namespace KoalaWiki.Provider.Sqlite.Migrations 6 | { 7 | /// 8 | public partial class AddFile : Migration 9 | { 10 | /// 11 | protected override void Up(MigrationBuilder migrationBuilder) 12 | { 13 | migrationBuilder.AddColumn( 14 | name: "Files", 15 | table: "ChatShareMessageItems", 16 | type: "TEXT", 17 | nullable: false, 18 | defaultValue: ""); 19 | 20 | migrationBuilder.CreateIndex( 21 | name: "IX_ChatShareMessageItems_WarehouseId", 22 | table: "ChatShareMessageItems", 23 | column: "WarehouseId"); 24 | } 25 | 26 | /// 27 | protected override void Down(MigrationBuilder migrationBuilder) 28 | { 29 | migrationBuilder.DropIndex( 30 | name: "IX_ChatShareMessageItems_WarehouseId", 31 | table: "ChatShareMessageItems"); 32 | 33 | migrationBuilder.DropColumn( 34 | name: "Files", 35 | table: "ChatShareMessageItems"); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Provider/KoalaWiki.Provider.Sqlite/Migrations/20250508100013_addRecommended.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.Migrations; 2 | 3 | #nullable disable 4 | 5 | namespace KoalaWiki.Provider.Sqlite.Migrations 6 | { 7 | /// 8 | public partial class addRecommended : Migration 9 | { 10 | /// 11 | protected override void Up(MigrationBuilder migrationBuilder) 12 | { 13 | migrationBuilder.AddColumn( 14 | name: "IsRecommended", 15 | table: "Warehouses", 16 | type: "INTEGER", 17 | nullable: false, 18 | defaultValue: false); 19 | } 20 | 21 | /// 22 | protected override void Down(MigrationBuilder migrationBuilder) 23 | { 24 | migrationBuilder.DropColumn( 25 | name: "IsRecommended", 26 | table: "Warehouses"); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Provider/KoalaWiki.Provider.Sqlite/Migrations/20250509063114_AddIsCompleted.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.Migrations; 2 | 3 | #nullable disable 4 | 5 | namespace KoalaWiki.Provider.Sqlite.Migrations 6 | { 7 | /// 8 | public partial class AddIsCompleted : Migration 9 | { 10 | /// 11 | protected override void Up(MigrationBuilder migrationBuilder) 12 | { 13 | migrationBuilder.AddColumn( 14 | name: "IsCompleted", 15 | table: "DocumentCatalogs", 16 | type: "INTEGER", 17 | nullable: false, 18 | defaultValue: false); 19 | } 20 | 21 | /// 22 | protected override void Down(MigrationBuilder migrationBuilder) 23 | { 24 | migrationBuilder.DropColumn( 25 | name: "IsCompleted", 26 | table: "DocumentCatalogs"); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Provider/KoalaWiki.Provider.Sqlite/Migrations/20250510081022_AddFiles.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.Migrations; 2 | 3 | #nullable disable 4 | 5 | namespace KoalaWiki.Provider.Sqlite.Migrations 6 | { 7 | /// 8 | public partial class AddFiles : Migration 9 | { 10 | /// 11 | protected override void Up(MigrationBuilder migrationBuilder) 12 | { 13 | migrationBuilder.AddColumn( 14 | name: "OptimizedDirectoryStructure", 15 | table: "Warehouses", 16 | type: "TEXT", 17 | nullable: false, 18 | defaultValue: ""); 19 | 20 | migrationBuilder.AddColumn( 21 | name: "DependentFile", 22 | table: "DocumentCatalogs", 23 | type: "TEXT", 24 | nullable: false, 25 | defaultValue: ""); 26 | 27 | migrationBuilder.AddColumn( 28 | name: "Prompt", 29 | table: "DocumentCatalogs", 30 | type: "TEXT", 31 | nullable: false, 32 | defaultValue: ""); 33 | } 34 | 35 | /// 36 | protected override void Down(MigrationBuilder migrationBuilder) 37 | { 38 | migrationBuilder.DropColumn( 39 | name: "OptimizedDirectoryStructure", 40 | table: "Warehouses"); 41 | 42 | migrationBuilder.DropColumn( 43 | name: "DependentFile", 44 | table: "DocumentCatalogs"); 45 | 46 | migrationBuilder.DropColumn( 47 | name: "Prompt", 48 | table: "DocumentCatalogs"); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Provider/KoalaWiki.Provider.Sqlite/Migrations/20250512065649_AddDeletedTime.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.EntityFrameworkCore.Migrations; 3 | 4 | #nullable disable 5 | 6 | namespace KoalaWiki.Provider.Sqlite.Migrations 7 | { 8 | /// 9 | public partial class AddDeletedTime : Migration 10 | { 11 | /// 12 | protected override void Up(MigrationBuilder migrationBuilder) 13 | { 14 | migrationBuilder.AddColumn( 15 | name: "DeletedTime", 16 | table: "DocumentCatalogs", 17 | type: "TEXT", 18 | nullable: true); 19 | 20 | migrationBuilder.AddColumn( 21 | name: "IsDeleted", 22 | table: "DocumentCatalogs", 23 | type: "INTEGER", 24 | nullable: false, 25 | defaultValue: false); 26 | 27 | migrationBuilder.CreateIndex( 28 | name: "IX_DocumentCatalogs_IsDeleted", 29 | table: "DocumentCatalogs", 30 | column: "IsDeleted"); 31 | } 32 | 33 | /// 34 | protected override void Down(MigrationBuilder migrationBuilder) 35 | { 36 | migrationBuilder.DropIndex( 37 | name: "IX_DocumentCatalogs_IsDeleted", 38 | table: "DocumentCatalogs"); 39 | 40 | migrationBuilder.DropColumn( 41 | name: "DeletedTime", 42 | table: "DocumentCatalogs"); 43 | 44 | migrationBuilder.DropColumn( 45 | name: "IsDeleted", 46 | table: "DocumentCatalogs"); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Provider/KoalaWiki.Provider.Sqlite/Migrations/20250512093510_AddCommitTitle.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.Migrations; 2 | 3 | #nullable disable 4 | 5 | namespace KoalaWiki.Provider.Sqlite.Migrations 6 | { 7 | /// 8 | public partial class AddCommitTitle : Migration 9 | { 10 | /// 11 | protected override void Up(MigrationBuilder migrationBuilder) 12 | { 13 | migrationBuilder.AddColumn( 14 | name: "Title", 15 | table: "DocumentCommitRecords", 16 | type: "TEXT", 17 | nullable: false, 18 | defaultValue: ""); 19 | } 20 | 21 | /// 22 | protected override void Down(MigrationBuilder migrationBuilder) 23 | { 24 | migrationBuilder.DropColumn( 25 | name: "Title", 26 | table: "DocumentCommitRecords"); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Provider/KoalaWiki.Provider.Sqlite/Migrations/20250515170011_AddUser.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.EntityFrameworkCore.Migrations; 3 | 4 | #nullable disable 5 | 6 | namespace KoalaWiki.Provider.Sqlite.Migrations 7 | { 8 | /// 9 | public partial class AddUser : Migration 10 | { 11 | /// 12 | protected override void Up(MigrationBuilder migrationBuilder) 13 | { 14 | migrationBuilder.CreateTable( 15 | name: "Users", 16 | columns: table => new 17 | { 18 | Id = table.Column(type: "TEXT", nullable: false), 19 | Name = table.Column(type: "TEXT", nullable: false), 20 | Email = table.Column(type: "TEXT", nullable: false), 21 | Password = table.Column(type: "TEXT", nullable: false), 22 | Role = table.Column(type: "TEXT", nullable: false), 23 | Avatar = table.Column(type: "TEXT", nullable: false), 24 | CreatedAt = table.Column(type: "TEXT", nullable: false), 25 | UpdatedAt = table.Column(type: "TEXT", nullable: true), 26 | LastLoginAt = table.Column(type: "TEXT", nullable: true), 27 | LastLoginIp = table.Column(type: "TEXT", nullable: true) 28 | }, 29 | constraints: table => 30 | { 31 | table.PrimaryKey("PK_Users", x => x.Id); 32 | }); 33 | 34 | migrationBuilder.CreateIndex( 35 | name: "IX_Users_CreatedAt", 36 | table: "Users", 37 | column: "CreatedAt"); 38 | 39 | migrationBuilder.CreateIndex( 40 | name: "IX_Users_Email", 41 | table: "Users", 42 | column: "Email"); 43 | 44 | migrationBuilder.CreateIndex( 45 | name: "IX_Users_LastLoginAt", 46 | table: "Users", 47 | column: "LastLoginAt"); 48 | 49 | migrationBuilder.CreateIndex( 50 | name: "IX_Users_Name", 51 | table: "Users", 52 | column: "Name"); 53 | } 54 | 55 | /// 56 | protected override void Down(MigrationBuilder migrationBuilder) 57 | { 58 | migrationBuilder.DropTable( 59 | name: "Users"); 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /Provider/KoalaWiki.Provider.Sqlite/Migrations/20250521190105_AddModel.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.Migrations; 2 | 3 | #nullable disable 4 | 5 | namespace KoalaWiki.Provider.Sqlite.Migrations 6 | { 7 | /// 8 | public partial class AddModel : Migration 9 | { 10 | /// 11 | protected override void Up(MigrationBuilder migrationBuilder) 12 | { 13 | migrationBuilder.AddColumn( 14 | name: "Model", 15 | table: "TrainingDatasets", 16 | type: "TEXT", 17 | nullable: false, 18 | defaultValue: ""); 19 | } 20 | 21 | /// 22 | protected override void Down(MigrationBuilder migrationBuilder) 23 | { 24 | migrationBuilder.DropColumn( 25 | name: "Model", 26 | table: "TrainingDatasets"); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Provider/KoalaWiki.Provider.Sqlite/Migrations/20250523101910_AddMCPHistory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.EntityFrameworkCore.Migrations; 3 | 4 | #nullable disable 5 | 6 | namespace KoalaWiki.Provider.Sqlite.Migrations 7 | { 8 | /// 9 | public partial class AddMCPHistory : Migration 10 | { 11 | /// 12 | protected override void Up(MigrationBuilder migrationBuilder) 13 | { 14 | migrationBuilder.CreateTable( 15 | name: "MCPHistories", 16 | columns: table => new 17 | { 18 | Id = table.Column(type: "TEXT", nullable: false, comment: "主键Id"), 19 | Question = table.Column(type: "TEXT", nullable: false), 20 | Answer = table.Column(type: "TEXT", nullable: false), 21 | UserId = table.Column(type: "TEXT", nullable: true), 22 | WarehouseId = table.Column(type: "TEXT", nullable: true), 23 | CostTime = table.Column(type: "INTEGER", nullable: false), 24 | Ip = table.Column(type: "TEXT", nullable: false), 25 | UserAgent = table.Column(type: "TEXT", nullable: false), 26 | CreatedAt = table.Column(type: "TEXT", nullable: false) 27 | }, 28 | constraints: table => 29 | { 30 | table.PrimaryKey("PK_MCPHistories", x => x.Id); 31 | }, 32 | comment: "MCP历史记录"); 33 | 34 | migrationBuilder.CreateIndex( 35 | name: "IX_MCPHistories_CreatedAt", 36 | table: "MCPHistories", 37 | column: "CreatedAt"); 38 | 39 | migrationBuilder.CreateIndex( 40 | name: "IX_MCPHistories_UserId", 41 | table: "MCPHistories", 42 | column: "UserId"); 43 | 44 | migrationBuilder.CreateIndex( 45 | name: "IX_MCPHistories_WarehouseId", 46 | table: "MCPHistories", 47 | column: "WarehouseId"); 48 | } 49 | 50 | /// 51 | protected override void Down(MigrationBuilder migrationBuilder) 52 | { 53 | migrationBuilder.DropTable( 54 | name: "MCPHistories"); 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /Provider/KoalaWiki.Provider.Sqlite/SqliteApplicationExtensions.cs: -------------------------------------------------------------------------------- 1 | using KoalaWiki.Core; 2 | using Microsoft.EntityFrameworkCore; 3 | using Microsoft.Extensions.Configuration; 4 | using Microsoft.Extensions.DependencyInjection; 5 | using Microsoft.Extensions.Logging; 6 | 7 | namespace KoalaWiki.Provider.Sqlite; 8 | 9 | 10 | public static class SqliteApplicationExtensions 11 | { 12 | public static IServiceCollection AddSqliteDbContext(this IServiceCollection services, 13 | IConfiguration configuration) 14 | { 15 | 16 | services.AddDataAccess(((provider, builder) => 17 | { 18 | builder.UseSqlite(configuration.GetConnectionString("Default")); 19 | 20 | // sql日志不输出控制台 21 | builder.UseLoggerFactory(LoggerFactory.Create(_ => { })); 22 | })); 23 | 24 | 25 | return services; 26 | } 27 | public static IServiceCollection AddSqliteDbContext(this IServiceCollection services, 28 | string connectionString) 29 | { 30 | 31 | services.AddDataAccess(((provider, builder) => 32 | { 33 | builder.UseSqlite(connectionString); 34 | 35 | // sql日志不输出控制台 36 | builder.UseLoggerFactory(LoggerFactory.Create(_ => { })); 37 | })); 38 | 39 | 40 | return services; 41 | } 42 | } -------------------------------------------------------------------------------- /Provider/KoalaWiki.Provider.Sqlite/SqliteContext.cs: -------------------------------------------------------------------------------- 1 | using KoalaWiki.Core.DataAccess; 2 | using Microsoft.EntityFrameworkCore; 3 | using Microsoft.EntityFrameworkCore.Diagnostics; 4 | 5 | namespace KoalaWiki.Provider.Sqlite; 6 | 7 | public class SqliteContext(DbContextOptions options) 8 | : KoalaWikiContext(options) 9 | { 10 | protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) 11 | { 12 | optionsBuilder.ConfigureWarnings(warnings => 13 | warnings.Ignore(RelationalEventId.PendingModelChangesWarning)); 14 | } 15 | } -------------------------------------------------------------------------------- /build-image.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | echo =========================================== 3 | echo Building KoalaWiki Docker Images 4 | echo =========================================== 5 | 6 | REM Enable Docker Buildx 7 | echo Enabling Docker Buildx... 8 | docker buildx create --use 9 | 10 | REM Build backend image 11 | echo Building backend image... 12 | docker buildx build ^ 13 | --platform linux/amd64,linux/arm64 ^ 14 | -t crpi-j9ha7sxwhatgtvj4.cn-shenzhen.personal.cr.aliyuncs.com/koala-ai/koala-wiki ^ 15 | -f src/KoalaWiki/Dockerfile ^ 16 | --push . 17 | if %ERRORLEVEL% NEQ 0 ( 18 | echo Error building backend image! 19 | exit /b %ERRORLEVEL% 20 | ) 21 | 22 | REM Build frontend image 23 | echo Building frontend image... 24 | pushd web 25 | docker buildx build ^ 26 | --platform linux/amd64,linux/arm64 ^ 27 | -t crpi-j9ha7sxwhatgtvj4.cn-shenzhen.personal.cr.aliyuncs.com/koala-ai/koala-wiki-web ^ 28 | -f Dockerfile ^ 29 | --push . 30 | if %ERRORLEVEL% NEQ 0 ( 31 | echo Error building frontend amd64 image! 32 | exit /b %ERRORLEVEL% 33 | ) 34 | popd 35 | 36 | echo =========================================== 37 | echo Images built and pushed successfully! 38 | echo =========================================== 39 | 40 | -------------------------------------------------------------------------------- /build-image.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo "===========================================" 4 | echo "Building KoalaWiki Docker Images with BuildKit Multi-platform" 5 | echo "===========================================" 6 | 7 | # Enable Docker Buildx with a new builder that supports multi-platform builds 8 | docker buildx create --name multiplatform-builder --use 9 | 10 | # Build and push backend images for multiple platforms at once 11 | echo "Building and pushing backend images for multiple platforms..." 12 | docker buildx build \ 13 | --platform linux/amd64,linux/arm64 \ 14 | -t crpi-j9ha7sxwhatgtvj4.cn-shenzhen.personal.cr.aliyuncs.com/koala-ai/koala-wiki:latest \ 15 | -t crpi-j9ha7sxwhatgtvj4.cn-shenzhen.personal.cr.aliyuncs.com/koala-ai/koala-wiki:amd64 \ 16 | -f src/KoalaWiki/Dockerfile \ 17 | --push . 18 | 19 | if [ $? -ne 0 ]; then 20 | echo "Error building and pushing backend images!" 21 | exit 1 22 | fi 23 | 24 | # Build and push frontend images for multiple platforms at once 25 | echo "Building and pushing frontend images for multiple platforms..." 26 | pushd web > /dev/null 27 | docker buildx build \ 28 | --platform linux/amd64,linux/arm64 \ 29 | -t crpi-j9ha7sxwhatgtvj4.cn-shenzhen.personal.cr.aliyuncs.com/koala-ai/koala-wiki-web:latest \ 30 | -t crpi-j9ha7sxwhatgtvj4.cn-shenzhen.personal.cr.aliyuncs.com/koala-ai/koala-wiki-web:amd64 \ 31 | -f Dockerfile \ 32 | --push . 33 | 34 | if [ $? -ne 0 ]; then 35 | echo "Error building and pushing frontend images!" 36 | exit 1 37 | fi 38 | popd > /dev/null 39 | 40 | # Clean up the builder 41 | docker buildx rm multiplatform-builder 42 | 43 | echo "===========================================" 44 | echo "All images built and pushed successfully!" 45 | echo "===========================================" 46 | -------------------------------------------------------------------------------- /build.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | chcp 65001 >nul 3 | echo 开始构建KoalaWiki项目... 4 | 5 | REM 复制启动脚本 6 | copy ".\start-backend.bat" ".\output\" >nul 7 | copy ".\start-frontend.bat" ".\output\" >nul 8 | 9 | REM 构建后端 10 | echo 构建后端... 11 | dotnet publish KoalaWiki.sln -c Release -o output\backend 12 | 13 | echo 构建前端... 14 | cd web 15 | call npm install 16 | call npm run build 17 | xcopy /E /I /Y .next ..\output\frontend\ 18 | cd .. 19 | 20 | xcopy /E /I /Y .next\static ..\output\frontend\standalone\.next\static\ 21 | 22 | pause -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo "开始构建KoalaWiki项目..." 4 | 5 | # 创建输出目录 6 | mkdir -p output/backend 7 | mkdir -p output/frontend 8 | 9 | # 复制启动脚本 10 | cp "./start-backend.sh" "./output/" 2>/dev/null || true 11 | cp "./start-frontend.sh" "./output/" 2>/dev/null || true 12 | 13 | echo "构建后端..." 14 | dotnet publish KoalaWiki.sln -c Release -o output/backend 15 | 16 | echo "构建前端..." 17 | cd web 18 | npm install 19 | npm run build 20 | cp -r .next ../output/frontend/ 21 | cd .. 22 | 23 | echo "KoalaWiki项目构建完成!" 24 | echo "后端输出目录: $(pwd)/output/backend" 25 | echo "前端输出目录: $(pwd)/output/frontend" -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | services: 2 | koalawiki: 3 | image: crpi-j9ha7sxwhatgtvj4.cn-shenzhen.personal.cr.aliyuncs.com/koala-ai/koala-wiki 4 | environment: 5 | - KOALAWIKI_REPOSITORIES=/repositories 6 | - TASK_MAX_SIZE_PER_USER=5 # 每个用户AI处理文档生成的最大数量 7 | - REPAIR_MERMAID=1 # 是否进行Mermaid修复,1修复,其余不修复 8 | - CHAT_MODEL=DeepSeek-V3 # 必须要支持function的模型 9 | - ANALYSIS_MODEL= # 分析模型,用于生成仓库目录结构,这个很重要,模型越强,生成的目录结构越好,为空则使用ChatModel 10 | # 分析模型建议使用GPT-4.1 , CHAT模型可以用其他模型生成文档,以节省 token 开销 11 | - CHAT_API_KEY= #您的APIkey 12 | - LANGUAGE= # 设置生成语言默认为“中文”, 英文可以填写 English 或 英文 13 | - ENDPOINT=https://api.token-ai.cn/v1 14 | - DB_TYPE=sqlite 15 | - DB_CONNECTION_STRING=Data Source=/data/KoalaWiki.db 16 | - UPDATE_INTERVAL=5 # 仓库增量更新间隔,单位天 17 | - EnableSmartFilter=true # 是否启用智能过滤,这可能影响AI得到仓库的文件目录 18 | - ENABLE_INCREMENTAL_UPDATE=true # 是否启用增量更新 19 | volumes: 20 | - ./repositories:/app/repositories 21 | - ./data:/data 22 | build: 23 | context: . 24 | dockerfile: src/KoalaWiki/Dockerfile 25 | 26 | koalawiki-web: 27 | image: crpi-j9ha7sxwhatgtvj4.cn-shenzhen.personal.cr.aliyuncs.com/koala-ai/koala-wiki-web 28 | environment: 29 | - NEXT_PUBLIC_API_URL=http://koalawiki:8080 # 用于提供给server的地址 30 | build: 31 | context: ./web 32 | dockerfile: Dockerfile 33 | 34 | nginx: # 需要nginx将前端和后端代理到一个端口 35 | image: crpi-j9ha7sxwhatgtvj4.cn-shenzhen.personal.cr.aliyuncs.com/koala-ai/nginx:alpine 36 | ports: 37 | - 8090:80 38 | volumes: 39 | - ./nginx/nginx.conf:/etc/nginx/conf.d/default.conf 40 | depends_on: 41 | - koalawiki 42 | - koalawiki-web 43 | 44 | -------------------------------------------------------------------------------- /img/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AIDotNet/OpenDeepWiki/ee597a3390e5b84bbd82b9066ac68aae6104cbbb/img/favicon.png -------------------------------------------------------------------------------- /img/mcp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AIDotNet/OpenDeepWiki/ee597a3390e5b84bbd82b9066ac68aae6104cbbb/img/mcp.png -------------------------------------------------------------------------------- /img/sealos/sealos-deploy.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AIDotNet/OpenDeepWiki/ee597a3390e5b84bbd82b9066ac68aae6104cbbb/img/sealos/sealos-deploy.jpg -------------------------------------------------------------------------------- /img/sealos/sealos-details.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AIDotNet/OpenDeepWiki/ee597a3390e5b84bbd82b9066ac68aae6104cbbb/img/sealos/sealos-details.jpg -------------------------------------------------------------------------------- /img/sealos/sealos-index.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AIDotNet/OpenDeepWiki/ee597a3390e5b84bbd82b9066ac68aae6104cbbb/img/sealos/sealos-index.jpg -------------------------------------------------------------------------------- /img/sealos/sealos-myapps.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AIDotNet/OpenDeepWiki/ee597a3390e5b84bbd82b9066ac68aae6104cbbb/img/sealos/sealos-myapps.jpg -------------------------------------------------------------------------------- /img/sealos/sealos-online-debug.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AIDotNet/OpenDeepWiki/ee597a3390e5b84bbd82b9066ac68aae6104cbbb/img/sealos/sealos-online-debug.jpg -------------------------------------------------------------------------------- /img/sealos/sealos-overview.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AIDotNet/OpenDeepWiki/ee597a3390e5b84bbd82b9066ac68aae6104cbbb/img/sealos/sealos-overview.jpg -------------------------------------------------------------------------------- /img/sealos/sealos-test.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AIDotNet/OpenDeepWiki/ee597a3390e5b84bbd82b9066ac68aae6104cbbb/img/sealos/sealos-test.jpg -------------------------------------------------------------------------------- /img/sealos/sealos-web.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AIDotNet/OpenDeepWiki/ee597a3390e5b84bbd82b9066ac68aae6104cbbb/img/sealos/sealos-web.jpg -------------------------------------------------------------------------------- /nginx/nginx.conf: -------------------------------------------------------------------------------- 1 | server { 2 | listen 80; 3 | server_name localhost; 4 | 5 | # 设置上传文件大小限制为 100MB 6 | client_max_body_size 100M; 7 | 8 | # 日志配置 9 | access_log /var/log/nginx/access.log; 10 | error_log /var/log/nginx/error.log; 11 | 12 | # 代理所有 /api/ 请求到后端服务 13 | location /api/ { 14 | proxy_pass http://koalawiki:8080/api/; 15 | proxy_http_version 1.1; 16 | proxy_set_header Upgrade $http_upgrade; 17 | proxy_set_header Connection 'upgrade'; 18 | proxy_set_header Host $host; 19 | proxy_set_header X-Real-IP $remote_addr; 20 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 21 | proxy_set_header X-Forwarded-Proto $scheme; 22 | proxy_cache_bypass $http_upgrade; 23 | } 24 | 25 | # 其他所有请求转发到前端服务 26 | location / { 27 | proxy_pass http://koalawiki-web:3000; 28 | proxy_http_version 1.1; 29 | proxy_set_header Upgrade $http_upgrade; 30 | proxy_set_header Connection 'upgrade'; 31 | proxy_set_header Host $host; 32 | proxy_set_header X-Real-IP $remote_addr; 33 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 34 | proxy_set_header X-Forwarded-Proto $scheme; 35 | proxy_cache_bypass $http_upgrade; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "@tailwindcss/postcss": "^4.1.7", 4 | "autoprefixer": "^10.4.21", 5 | "postcss": "^8.5.3", 6 | "tailwindcss": "^4.1.7" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /scripts/sealos/README.zh-CN.md: -------------------------------------------------------------------------------- 1 | # 使用模板将 OpenDeepWiki 一键部署为 Sealos 应用暴露到公网 2 | 3 | ### 1. 进入 Sealos 应用商店 4 | 打开 Sealos 平台后,在左侧导航栏点击 **「应用商店」**,进入模板市场。 5 | ![进入应用商店界面](../../img/sealos/sealos-index.jpg) 6 | 7 | --- 8 | 9 | ### 2. 打开模板调试模式 10 | 点击左下角的 **「我的应用」**,然后在右上角找到 **「在线调试模板」** 按钮。 11 | ![打开我的应用](../../img/sealos/sealos-myapps.jpg) 12 | ![在线调试模板入口](../../img/sealos/sealos-online-debug.jpg) 13 | 14 | --- 15 | 16 | ### 3. 试运行部署 17 | * 将你的模板 [template.yaml](./sealos-template.yaml) 文件内容复制到左侧输入框。 18 | * 在右侧参数输入区域填写相关配置(如 `api_key`、`analysis_model` 等)。 19 | * 点击右上角 **「试运行部署」**,检查右侧控制台是否显示 **“部署通过”** 等字样。 20 | 21 | ![粘贴 YAML 文件界面](../../img/sealos/sealos-test.jpg) 22 | 23 | --- 24 | 25 | ### 4. 正式部署应用 26 | 确认无误后,点击 **「正式部署」**,等待应用状态变为 **“Running”**。 27 | ![正式部署成功界面](../../img/sealos/sealos-deploy.jpg) 28 | 29 | --- 30 | 31 | ### 5. 查看应用详情 32 | 部署成功后,点击应用名称进入应用详情页面,查看应用的外网地址和日志。 33 | ![查看部署情况](../../img/sealos/sealos-overview.jpg) 34 | ![查看应用详情](../../img/sealos/sealos-details.jpg) 35 | ![打开外网地址](../../img/sealos/sealos-web.jpg) 36 | 37 | --- 38 | 39 | ### 注意事项 40 | - 参数需参考模板要求填写。 41 | - 部署完成后,可通过「应用详情」查看日志和外网地址。 -------------------------------------------------------------------------------- /src/KoalaWiki/CodeMap/CodeSegment.cs: -------------------------------------------------------------------------------- 1 | namespace KoalaWiki.CodeMap; 2 | 3 | /// 4 | /// 代码段数据模型 5 | /// 6 | public class CodeSegment 7 | { 8 | public string Type { get; set; } = ""; // 代码段类型(类、方法、函数等) 9 | 10 | public string Name { get; set; } = ""; // 代码段名称 11 | 12 | public string Code { get; set; } = ""; // 代码内容 13 | 14 | public int StartLine { get; set; } // 起始行号 15 | 16 | public int EndLine { get; set; } // 结束行号 17 | 18 | public string Namespace { get; set; } = ""; // 命名空间 19 | 20 | public string ClassName { get; set; } = ""; // 类名(对于方法) 21 | 22 | public string Documentation { get; set; } = ""; // 文档注释 23 | 24 | public string ReturnType { get; set; } = ""; // 返回类型 25 | 26 | public string Parameters { get; set; } = ""; // 参数列表 27 | 28 | public List Dependencies { get; set; } = new List(); // 依赖项 29 | 30 | public string Modifiers { get; set; } = ""; // 修饰符(public, private等) 31 | } -------------------------------------------------------------------------------- /src/KoalaWiki/Constant.cs: -------------------------------------------------------------------------------- 1 | namespace KoalaWiki; 2 | 3 | public static class Constant 4 | { 5 | public static string GitPath => Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "repositories"); 6 | } -------------------------------------------------------------------------------- /src/KoalaWiki/DataMigration/DataMigrationTask.cs: -------------------------------------------------------------------------------- 1 | using KoalaWiki.Core.DataAccess; 2 | using KoalaWiki.Domains.Users; 3 | using Microsoft.EntityFrameworkCore; 4 | 5 | namespace KoalaWiki.DataMigration; 6 | 7 | public class DataMigrationTask(IServiceProvider service) : BackgroundService 8 | { 9 | protected override async Task ExecuteAsync(CancellationToken stoppingToken) 10 | { 11 | await Task.Delay(100, stoppingToken); 12 | 13 | await using var scope = service.CreateAsyncScope(); 14 | 15 | var dbContext = scope.ServiceProvider.GetService(); 16 | 17 | // 判断是否存在账号 18 | if (await dbContext!.Users.AnyAsync(stoppingToken)) 19 | { 20 | // 如果存在账号,则不执行迁移 21 | return; 22 | } 23 | 24 | // 迁移数据库 25 | var admin = new User 26 | { 27 | Id = Guid.NewGuid().ToString("N"), 28 | Name = "admin", 29 | Password = "admin", 30 | Email = "239573049@qq.com", 31 | CreatedAt = DateTime.UtcNow, 32 | UpdatedAt = DateTime.UtcNow, 33 | Avatar = "https://avatars.githubusercontent.com/u/61819790?v=4", 34 | Role = "admin", 35 | }; 36 | 37 | // 创建管理员账号 38 | await dbContext.Users.AddAsync(admin, stoppingToken); 39 | await dbContext.SaveChangesAsync(stoppingToken); 40 | 41 | 42 | } 43 | } -------------------------------------------------------------------------------- /src/KoalaWiki/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/aspnet:9.0 AS base 2 | USER root 3 | WORKDIR /app 4 | EXPOSE 8080 5 | EXPOSE 8081 6 | 7 | FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build 8 | ARG BUILD_CONFIGURATION=Release 9 | ARG TARGETARCH 10 | WORKDIR /src 11 | COPY ["src/KoalaWiki/KoalaWiki.csproj", "src/KoalaWiki/"] 12 | COPY ["NuGet.Config", "."] 13 | RUN dotnet restore "src/KoalaWiki/KoalaWiki.csproj" -a $TARGETARCH 14 | COPY . . 15 | WORKDIR "/src/src/KoalaWiki" 16 | RUN dotnet build "./KoalaWiki.csproj" -c $BUILD_CONFIGURATION -a $TARGETARCH -o /app/build 17 | 18 | FROM build AS publish 19 | ARG BUILD_CONFIGURATION=Release 20 | RUN dotnet publish "./KoalaWiki.csproj" -c $BUILD_CONFIGURATION -a $TARGETARCH -o /app/publish /p:UseAppHost=false 21 | 22 | FROM base AS final 23 | WORKDIR /app 24 | COPY --from=publish /app/publish . 25 | ENTRYPOINT ["dotnet", "KoalaWiki.dll"] 26 | 27 | -------------------------------------------------------------------------------- /src/KoalaWiki/Dto/ChatShareMessageInput.cs: -------------------------------------------------------------------------------- 1 | namespace KoalaWiki.Dto; 2 | 3 | public class ChatShareMessageInput 4 | { 5 | public bool IsDeep { get; set; } = false; 6 | 7 | public string Owner { get; set; } 8 | 9 | public string Name { get; set; } 10 | 11 | public string Message { get; set; } 12 | } -------------------------------------------------------------------------------- /src/KoalaWiki/Dto/CompletionsInput.cs: -------------------------------------------------------------------------------- 1 | namespace KoalaWiki.Dto; 2 | 3 | public class CompletionsInput 4 | { 5 | /// 6 | /// 关联id 7 | /// 8 | public string ChatShareMessageId { get; set; } 9 | 10 | public string Question { get; set; } 11 | 12 | public List Messages { get; set; } = []; 13 | } 14 | 15 | public class CompletionsMessageInput 16 | { 17 | public string Role { get; set; } 18 | 19 | public string Content { get; set; } 20 | } -------------------------------------------------------------------------------- /src/KoalaWiki/Dto/CreateCatalogInput.cs: -------------------------------------------------------------------------------- 1 | namespace KoalaWiki.Dto; 2 | 3 | public class CreateCatalogInput 4 | { 5 | /// 6 | /// 目录名称 7 | /// 8 | public string Name { get; set; } = string.Empty; 9 | 10 | /// 11 | /// 路由 12 | /// 13 | public string Url { get; set; } = string.Empty; 14 | 15 | /// 16 | /// 目录描述 17 | /// 18 | public string Description { get; set; } = string.Empty; 19 | 20 | /// 21 | /// 目录父级Id 22 | /// 23 | /// 24 | public string? ParentId { get; set; } = string.Empty; 25 | 26 | /// 27 | /// 当前目录排序 28 | /// 29 | public int Order { get; set; } = 0; 30 | 31 | /// 32 | /// 文档id 33 | /// 34 | public string DucumentId { get; set; } = string.Empty; 35 | 36 | public string WarehouseId { get; set; } = string.Empty; 37 | 38 | public string Prompt { get; set; } = string.Empty; 39 | 40 | public List DependentFile { get; set; } = new(); 41 | } -------------------------------------------------------------------------------- /src/KoalaWiki/Dto/GenerateFileContentInput.cs: -------------------------------------------------------------------------------- 1 | namespace KoalaWiki.Dto; 2 | 3 | public class GenerateFileContentInput 4 | { 5 | public string Id { get; set; } = string.Empty; 6 | 7 | public string Prompt { get; set; } = string.Empty; 8 | } -------------------------------------------------------------------------------- /src/KoalaWiki/Dto/LoginDto.cs: -------------------------------------------------------------------------------- 1 | using KoalaWiki.Domains.Users; 2 | 3 | namespace KoalaWiki.Dto; 4 | 5 | public record LoginDto(bool Success, string Token, string? RefreshToken, User? User, string? ErrorMessage); -------------------------------------------------------------------------------- /src/KoalaWiki/Dto/PageDto.cs: -------------------------------------------------------------------------------- 1 | namespace KoalaWiki.Dto; 2 | 3 | public class PageDto 4 | { 5 | public int Total { get; set; } 6 | 7 | public IList Items { get; set; } = new List(); 8 | 9 | public PageDto(int total, IList items) 10 | { 11 | Total = total; 12 | Items = items; 13 | } 14 | } -------------------------------------------------------------------------------- /src/KoalaWiki/Dto/RegisterInput.cs: -------------------------------------------------------------------------------- 1 | namespace KoalaWiki.Dto; 2 | 3 | public class RegisterInput 4 | { 5 | /// 6 | /// 用户名 7 | /// 8 | public string UserName { get; set; } = string.Empty; 9 | 10 | /// 11 | /// 密码 12 | /// 13 | public string Password { get; set; } = string.Empty; 14 | 15 | /// 16 | /// 邮箱 17 | /// 18 | public string Email { get; set; } = string.Empty; 19 | } -------------------------------------------------------------------------------- /src/KoalaWiki/Dto/RepoExtendedInfo.cs: -------------------------------------------------------------------------------- 1 | namespace KoalaWiki.Dto; 2 | 3 | 4 | public class RepoExtendedInfo 5 | { 6 | public bool Success { get; set; } 7 | public int Stars { get; set; } 8 | public int Forks { get; set; } 9 | public string AvatarUrl { get; set; } = string.Empty; 10 | public string OwnerUrl { get; set; } = string.Empty; 11 | public string RepoUrl { get; set; } = string.Empty; 12 | public string? Language { get; set; } 13 | public string? License { get; set; } 14 | public string? Description { get; set; } 15 | public string? Error { get; set; } 16 | } 17 | -------------------------------------------------------------------------------- /src/KoalaWiki/Dto/ResultDto.cs: -------------------------------------------------------------------------------- 1 | namespace KoalaWiki.Dto; 2 | 3 | public class ResultDto 4 | { 5 | public int Code { get; set; } 6 | 7 | public string Message { get; set; } = string.Empty; 8 | 9 | public T Data { get; set; } = default!; 10 | 11 | public ResultDto() 12 | { 13 | Code = 200; 14 | Message = "success"; 15 | } 16 | 17 | public static ResultDto Success(T data) 18 | { 19 | return new ResultDto 20 | { 21 | Code = 200, 22 | Message = "success", 23 | Data = data 24 | }; 25 | } 26 | 27 | public static ResultDto Fail(string message) 28 | { 29 | return new ResultDto 30 | { 31 | Code = 500, 32 | Message = message, 33 | Data = default! 34 | }; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/KoalaWiki/Dto/SaveFileContentInput.cs: -------------------------------------------------------------------------------- 1 | namespace KoalaWiki.Dto; 2 | 3 | public class SaveFileContentInput 4 | { 5 | public string Id { get; set; } = string.Empty; 6 | 7 | public string Content { get; set; } = string.Empty; 8 | } -------------------------------------------------------------------------------- /src/KoalaWiki/Dto/StartTaskInput.cs: -------------------------------------------------------------------------------- 1 | namespace KoalaWiki.Dto; 2 | 3 | public class StartTaskInput 4 | { 5 | public string TaskId { get; set; } 6 | 7 | public string Prompt { get; set; } 8 | } -------------------------------------------------------------------------------- /src/KoalaWiki/Dto/WarehouseInput.cs: -------------------------------------------------------------------------------- 1 | namespace KoalaWiki.Dto; 2 | 3 | public class WarehouseInput 4 | { 5 | /// 6 | /// 仓库地址 7 | /// 8 | /// 9 | public string Address { get; set; } 10 | 11 | /// 12 | /// 分支 13 | /// 14 | /// 15 | public string Branch { get; set; } 16 | 17 | /// 18 | /// 私有化git账号 19 | /// 20 | public string? GitUserName { get; set; } 21 | 22 | /// 23 | /// 私有化git密码 24 | /// 25 | public string? GitPassword { get; set; } 26 | 27 | /// 28 | /// 私有化git邮箱 29 | /// 30 | public string? Email { get; set; } 31 | } -------------------------------------------------------------------------------- /src/KoalaWiki/Extensions/DbContextExtensions.cs: -------------------------------------------------------------------------------- 1 | using KoalaWiki.Provider.PostgreSQL; 2 | using KoalaWiki.Provider.Sqlite; 3 | using KoalaWiki.Provider.SqlServer; 4 | 5 | namespace KoalaWiki.Extensions; 6 | 7 | public static class DbContextExtensions 8 | { 9 | public static IServiceCollection AddDbContext(this IServiceCollection services, 10 | IConfiguration configuration) 11 | { 12 | var dbType = Environment.GetEnvironmentVariable("DB_TYPE").GetTrimmedValueOrEmpty(); 13 | var dbConnectionString = Environment.GetEnvironmentVariable("DB_CONNECTION_STRING").GetTrimmedValueOrEmpty(); 14 | 15 | if (string.IsNullOrEmpty(dbType) || string.IsNullOrEmpty(dbConnectionString)) 16 | { 17 | var dbTypeFromConfig = configuration.GetConnectionString("type")?.ToLower(); 18 | if (dbTypeFromConfig == "postgres") 19 | { 20 | AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true); 21 | AppContext.SetSwitch("Npgsql.DisableDateTimeInfinityConversions", true); 22 | services.AddPostgreSQLDbContext(configuration); 23 | } 24 | else if (dbTypeFromConfig == "sqlserver") 25 | { 26 | services.AddSqlServerDbContext(configuration); 27 | } 28 | else 29 | { 30 | services.AddSqliteDbContext(configuration); 31 | } 32 | 33 | return services; 34 | } 35 | 36 | if (dbType.Equals("postgres", StringComparison.OrdinalIgnoreCase)) 37 | { 38 | AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true); 39 | AppContext.SetSwitch("Npgsql.DisableDateTimeInfinityConversions", true); 40 | services.AddPostgreSQLDbContext(dbConnectionString); 41 | } 42 | else if (dbType.Equals("sqlserver", StringComparison.OrdinalIgnoreCase)) 43 | { 44 | services.AddSqlServerDbContext(dbConnectionString); 45 | } 46 | else 47 | { 48 | services.AddSqliteDbContext(dbConnectionString); 49 | } 50 | 51 | return services; 52 | } 53 | } -------------------------------------------------------------------------------- /src/KoalaWiki/Extensions/GlobalMiddleware.cs: -------------------------------------------------------------------------------- 1 | using System.IdentityModel.Tokens.Jwt; 2 | using System.Security.Claims; 3 | using KoalaWiki.Options; 4 | using Microsoft.IdentityModel.Tokens; 5 | 6 | namespace KoalaWiki.Extensions; 7 | 8 | /// 9 | /// 全局中间件 10 | /// 11 | public class GlobalMiddleware(ILogger logger) : IMiddleware 12 | { 13 | public async Task InvokeAsync(HttpContext context, RequestDelegate next) 14 | { 15 | try 16 | { 17 | await next(context); 18 | } 19 | catch (Exception e) 20 | { 21 | logger.LogError(e, "An error occurred while processing the request."); 22 | context.Response.StatusCode = 500; 23 | context.Response.ContentType = "application/json"; 24 | await context.Response.WriteAsJsonAsync(new 25 | { 26 | code = 500, 27 | message = e.Message 28 | }); 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /src/KoalaWiki/Extensions/SitemapExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | using KoalaWiki.Core.DataAccess; 3 | using KoalaWiki.Entities; 4 | using Microsoft.EntityFrameworkCore; 5 | 6 | namespace KoalaWiki.Extensions; 7 | 8 | public static class SitemapExtensions 9 | { 10 | private const string UrlTemplate = 11 | "{0}{1}{2}"; 12 | 13 | private static async Task ExecuteAsync(this IKoalaWikiContext koala, HttpContext context) 14 | { 15 | // 先获取所有的仓库 16 | var warehouses = await koala.Warehouses 17 | .AsNoTracking() 18 | .Where(x => x.Status == WarehouseStatus.Completed) 19 | .ToListAsync(); 20 | 21 | // 获取上面仓库的所有catalogs 22 | var catalogs = await koala.DocumentCatalogs 23 | .AsNoTracking() 24 | .Where(x => warehouses.Select(w => w.Id).Contains(x.WarehouseId)) 25 | .ToListAsync(); 26 | 27 | var sb = new StringBuilder(); 28 | // 关键xml 29 | foreach (var warehouseUrl in warehouses.Select(warehouse => string.Format(UrlTemplate, 30 | $"{context.Request.Scheme}://{context.Request.Host}/wiki/{warehouse.OrganizationName}/{warehouse.Name}", 31 | "weekly", 32 | "0.5"))) 33 | { 34 | sb.Append(warehouseUrl); 35 | } 36 | 37 | foreach (var catalogUrl in from catalog in catalogs let warehouse = warehouses 38 | .FirstOrDefault(x => x.Id == catalog.WarehouseId) select string.Format(UrlTemplate, 39 | $"{context.Request.Scheme}://{context.Request.Host}/wiki/{warehouse?.OrganizationName}/{warehouse?.Name}/{catalog.Url}", 40 | "weekly", 41 | "0.5")) 42 | { 43 | sb.Append(catalogUrl); 44 | } 45 | 46 | await context.Response.WriteAsync( 47 | $""" 48 | {sb} 49 | """); 50 | } 51 | 52 | public static IEndpointRouteBuilder MapSitemap(this IEndpointRouteBuilder app) 53 | { 54 | app.MapGet("/sitemap.xml", ExecuteAsync); 55 | app.MapGet("/api/sitemap.xml", ExecuteAsync); 56 | 57 | return app; 58 | } 59 | } -------------------------------------------------------------------------------- /src/KoalaWiki/Extensions/StringExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.CompilerServices; 2 | 3 | namespace KoalaWiki.Extensions 4 | { 5 | public static class StringExtensions 6 | { 7 | /// 8 | /// 对字符串进行安全的 Trim 处理。如果字符串为 null 或空白,则返回空字符串; 9 | /// 否则返回去除首尾空白后的字符串。 10 | /// 11 | /// 要处理的字符串(可为空) 12 | /// Trim 后的字符串,或空字符串 13 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 14 | public static string GetTrimmedValueOrEmpty(this string? value) 15 | { 16 | return !string.IsNullOrWhiteSpace(value) ? value.Trim() : value ?? string.Empty; 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/KoalaWiki/Functions/RagFunction.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel; 2 | using KoalaWiki.CodeMap; 3 | using Microsoft.SemanticKernel; 4 | 5 | namespace KoalaWiki.Functions; 6 | 7 | public class RagFunction(string warehouseId) 8 | { 9 | [KernelFunction, Description("可以帮助用户搜索当前代码仓库的代码或文档")] 10 | public async Task SearchCodeAsync( 11 | [Description("更接近需要的代码或文档的描述,例如:搜索一个函数,或者搜索一个类")] 12 | string query, double minRelevance = 0.3) 13 | { 14 | var result = new List(); 15 | 16 | var enhancedCodeIndexer = new EnhancedCodeIndexer(); 17 | 18 | var value = await enhancedCodeIndexer.SearchSimilarCodeAsync(query, warehouseId, 3, minRelevance); 19 | string prompt = string.Empty; 20 | foreach (var item in value) 21 | { 22 | prompt += 23 | $""" 24 | 25 | {item.Code} 26 | 27 | 28 | 29 | {item.Description} 30 | 31 | 32 | 代码引用: 33 | 34 | {item.references} 35 | 36 | """; 37 | } 38 | 39 | return prompt; 40 | } 41 | } -------------------------------------------------------------------------------- /src/KoalaWiki/Git/GitRepositoryInfo.cs: -------------------------------------------------------------------------------- 1 | namespace KoalaWiki.Git; 2 | 3 | public record GitRepositoryInfo( 4 | string LocalPath, 5 | string RepositoryName, 6 | string Organization, 7 | string BranchName, 8 | string CommitTime, 9 | string CommitAuthor, 10 | string CommitMessage, 11 | string Version); -------------------------------------------------------------------------------- /src/KoalaWiki/GlobalUsing.cs: -------------------------------------------------------------------------------- 1 | global using KoalaWiki.CodeMap; 2 | global using KoalaWiki.Core.DataAccess; 3 | global using KoalaWiki.DataMigration; 4 | global using KoalaWiki.Extensions; 5 | global using KoalaWiki.Git; 6 | global using KoalaWiki.Infrastructure; 7 | global using KoalaWiki.KoalaWarehouse; 8 | global using KoalaWiki.MCP; 9 | global using KoalaWiki.Options; 10 | global using KoalaWiki.Services; 11 | global using KoalaWiki.WarehouseProcessing; 12 | global using Mapster; 13 | global using Microsoft.AspNetCore.Authentication.JwtBearer; 14 | global using Microsoft.AspNetCore.Http.Features; 15 | global using Microsoft.IdentityModel.Tokens; 16 | global using Scalar.AspNetCore; 17 | global using Serilog; -------------------------------------------------------------------------------- /src/KoalaWiki/Infrastructure/ResultFilter.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | 3 | namespace KoalaWiki.Infrastructure; 4 | 5 | public class ResultFilter : IEndpointFilter 6 | { 7 | public async ValueTask InvokeAsync(EndpointFilterInvocationContext context, EndpointFilterDelegate next) 8 | { 9 | var value = await next(context); 10 | 11 | if (value is EmptyResult) 12 | { 13 | return null; 14 | } 15 | 16 | return new 17 | { 18 | code = 200, 19 | data = value, 20 | }; 21 | } 22 | } -------------------------------------------------------------------------------- /src/KoalaWiki/Infrastructure/UserContext.cs: -------------------------------------------------------------------------------- 1 | using System.Security.Claims; 2 | using KoalaWiki.Core.DataAccess; 3 | 4 | namespace KoalaWiki.Infrastructure; 5 | 6 | /// 7 | /// 用户上下文实现 8 | /// 9 | public class UserContext : IUserContext 10 | { 11 | private readonly IHttpContextAccessor _httpContextAccessor; 12 | 13 | /// 14 | /// 构造函数 15 | /// 16 | /// HTTP上下文访问器 17 | public UserContext(IHttpContextAccessor httpContextAccessor) 18 | { 19 | _httpContextAccessor = httpContextAccessor; 20 | } 21 | 22 | /// 23 | /// 获取当前用户ID 24 | /// 25 | public string? CurrentUserId => _httpContextAccessor.HttpContext?.User?.FindFirstValue(ClaimTypes.NameIdentifier); 26 | 27 | /// 28 | /// 获取当前用户名 29 | /// 30 | public string? CurrentUserName => _httpContextAccessor.HttpContext?.User?.FindFirstValue(ClaimTypes.Name); 31 | 32 | /// 33 | /// 获取当前用户邮箱 34 | /// 35 | public string? CurrentUserEmail => _httpContextAccessor.HttpContext?.User?.FindFirstValue(ClaimTypes.Email); 36 | 37 | /// 38 | /// 获取当前用户角色 39 | /// 40 | public string? CurrentUserRole => _httpContextAccessor.HttpContext?.User?.FindFirstValue(ClaimTypes.Role); 41 | 42 | /// 43 | /// 判断用户是否已认证 44 | /// 45 | public bool IsAuthenticated => _httpContextAccessor.HttpContext?.User?.Identity?.IsAuthenticated ?? false; 46 | 47 | /// 48 | /// 判断用户是否是管理员 49 | /// 50 | public bool IsAdmin => CurrentUserRole == "admin"; 51 | } -------------------------------------------------------------------------------- /src/KoalaWiki/KoalaWarehouse/DocumentContext.cs: -------------------------------------------------------------------------------- 1 | namespace KoalaWiki.KoalaWarehouse; 2 | 3 | public class DocumentContext 4 | { 5 | private static readonly AsyncLocal _documentHolder = new(); 6 | 7 | public static DocumentStore? DocumentStore 8 | { 9 | get => _documentHolder.Value?.DocumentStore; 10 | set 11 | { 12 | _documentHolder.Value ??= new DocumentHolder(); 13 | 14 | _documentHolder.Value.DocumentStore = value; 15 | } 16 | } 17 | 18 | 19 | private class DocumentHolder 20 | { 21 | public DocumentStore DocumentStore { get; set; } = new(); 22 | } 23 | } 24 | 25 | public class DocumentStore 26 | { 27 | public List Files { get; set; } = new(); 28 | } -------------------------------------------------------------------------------- /src/KoalaWiki/KoalaWarehouse/DocumentResultCatalogue.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel; 2 | 3 | namespace KoalaWiki.KoalaWarehouse; 4 | 5 | public class DocumentResultCatalogue 6 | { 7 | public List items { get; set; } = new(); 8 | } 9 | 10 | public class DocumentResultCatalogueItem 11 | { 12 | public string name { get; set; } 13 | 14 | public string title { get; set; } 15 | 16 | public string prompt {get; set;} 17 | 18 | public string[] dependent_file { get; set; } 19 | 20 | public List children { get; set; } = new(); 21 | } 22 | -------------------------------------------------------------------------------- /src/KoalaWiki/KoalaWarehouse/DocumentsService.Commit.cs: -------------------------------------------------------------------------------- 1 | using System.Text.RegularExpressions; 2 | using LibGit2Sharp; 3 | using Microsoft.SemanticKernel; 4 | using Newtonsoft.Json; 5 | 6 | namespace KoalaWiki.KoalaWarehouse; 7 | 8 | public partial class DocumentsService 9 | { 10 | /// 11 | /// 生成更新日志 12 | /// 13 | public async Task> GenerateUpdateLogAsync(string gitPath, 14 | string readme, string gitRepositoryUrl, string branch, Kernel kernel) 15 | { 16 | // 读取git log 17 | using var repo = new Repository(gitPath, new RepositoryOptions()); 18 | 19 | var log = repo.Commits 20 | .OrderByDescending(x => x.Committer.When) 21 | .Take(20) 22 | .OrderBy(x => x.Committer.When) 23 | .ToList(); 24 | 25 | string commitMessage = string.Empty; 26 | foreach (var commit in log) 27 | { 28 | commitMessage += "提交人:" + commit.Committer.Name + "\n提交内容\n\n" + commit.Message + 29 | ""; 30 | 31 | commitMessage += "\n提交时间:" + commit.Committer.When.ToString("yyyy-MM-dd HH:mm:ss") + "\n"; 32 | } 33 | 34 | var plugin = kernel.Plugins["CodeAnalysis"]["CommitAnalyze"]; 35 | 36 | var str = string.Empty; 37 | await foreach (var item in kernel.InvokeStreamingAsync(plugin, new KernelArguments() 38 | { 39 | ["readme"] = readme, 40 | ["git_repository"] = gitRepositoryUrl, 41 | ["commit_message"] = commitMessage, 42 | ["branch"] = branch 43 | })) 44 | { 45 | str += item; 46 | } 47 | 48 | var regex = new Regex(@"(.*?)", 49 | RegexOptions.Singleline); 50 | 51 | var match = regex.Match(str); 52 | 53 | if (match.Success) 54 | { 55 | // 提取到的内容 56 | str = match.Groups[1].Value; 57 | } 58 | 59 | var result = JsonConvert.DeserializeObject>(str); 60 | 61 | return result; 62 | } 63 | 64 | public class CommitResultDto 65 | { 66 | public DateTime date { get; set; } 67 | 68 | public string title { get; set; } 69 | 70 | public string description { get; set; } 71 | } 72 | } -------------------------------------------------------------------------------- /src/KoalaWiki/KoalaWarehouse/PathInfo.cs: -------------------------------------------------------------------------------- 1 | namespace KoalaWiki.KoalaWarehouse; 2 | 3 | public class PathInfo 4 | { 5 | public string Path { get; set; } 6 | 7 | public string Name { get; set; } 8 | 9 | public string Type { get; set; } 10 | } -------------------------------------------------------------------------------- /src/KoalaWiki/KoalaWiki.sln: -------------------------------------------------------------------------------- 1 | Microsoft Visual Studio Solution File, Format Version 12.00 2 | # Visual Studio Version 17 3 | VisualStudioVersion = 17.5.2.0 4 | MinimumVisualStudioVersion = 10.0.40219.1 5 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "KoalaWiki", "KoalaWiki.csproj", "{87BAB4B4-C4ED-F373-3BB8-1538658234D5}" 6 | EndProject 7 | Global 8 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 9 | Debug|Any CPU = Debug|Any CPU 10 | Release|Any CPU = Release|Any CPU 11 | EndGlobalSection 12 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 13 | {87BAB4B4-C4ED-F373-3BB8-1538658234D5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 14 | {87BAB4B4-C4ED-F373-3BB8-1538658234D5}.Debug|Any CPU.Build.0 = Debug|Any CPU 15 | {87BAB4B4-C4ED-F373-3BB8-1538658234D5}.Release|Any CPU.ActiveCfg = Release|Any CPU 16 | {87BAB4B4-C4ED-F373-3BB8-1538658234D5}.Release|Any CPU.Build.0 = Release|Any CPU 17 | EndGlobalSection 18 | GlobalSection(SolutionProperties) = preSolution 19 | HideSolutionNode = FALSE 20 | EndGlobalSection 21 | GlobalSection(ExtensibilityGlobals) = postSolution 22 | SolutionGuid = {BE9DDBC0-7B01-4ADE-8CEF-77C1E3E14509} 23 | EndGlobalSection 24 | EndGlobal 25 | -------------------------------------------------------------------------------- /src/KoalaWiki/Options/DocumentOptions.cs: -------------------------------------------------------------------------------- 1 | namespace KoalaWiki.Options; 2 | 3 | public class DocumentOptions 4 | { 5 | public const string Name = "Document"; 6 | 7 | /// 8 | /// 是否启用增量更新 9 | /// 10 | /// 11 | public static bool EnableIncrementalUpdate { get; set; } = true; 12 | 13 | /// 14 | /// 排除的文件 15 | /// 16 | /// 17 | public static string[] ExcludedFiles { get; set; } = []; 18 | 19 | /// 20 | /// 排除的文件夹 21 | /// 22 | /// 23 | public static string[] ExcludedFolders { get; set; } = []; 24 | 25 | /// 26 | /// 是否启用智能过滤 27 | /// 28 | /// 29 | public static bool EnableSmartFilter { get; set; } = true; 30 | } -------------------------------------------------------------------------------- /src/KoalaWiki/Options/GithubOptions.cs: -------------------------------------------------------------------------------- 1 | namespace KoalaWiki.Options; 2 | 3 | public class GithubOptions 4 | { 5 | public static string ClientId { get; set; } 6 | 7 | public static string ClientSecret { get; set; } 8 | 9 | public static string Token { get; set; } 10 | 11 | 12 | public static void InitConfig(IConfiguration configuration) 13 | { 14 | ClientId = configuration["Github:ClientId"] ?? configuration["GITHUB_CLIENT_ID"]; 15 | ClientSecret = configuration["Github:ClientSecret"] ?? configuration["GITHUB_CLIENT_SECRET"]; 16 | Token = configuration["Github:Token"] ?? configuration["GITHUB_TOKEN"]; 17 | } 18 | } -------------------------------------------------------------------------------- /src/KoalaWiki/Options/JwtOptions.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | using Microsoft.IdentityModel.Tokens; 3 | 4 | namespace KoalaWiki.Options; 5 | 6 | /// 7 | /// JWT配置选项 8 | /// 9 | public class JwtOptions 10 | { 11 | /// 12 | /// 配置名称 13 | /// 14 | public const string Name = "Jwt"; 15 | 16 | /// 17 | /// 密钥 18 | /// 19 | public string Secret { get; set; } = string.Empty; 20 | 21 | /// 22 | /// 颁发者 23 | /// 24 | public string Issuer { get; set; } = string.Empty; 25 | 26 | /// 27 | /// 接收者 28 | /// 29 | public string Audience { get; set; } = string.Empty; 30 | 31 | /// 32 | /// 过期时间(分钟) 33 | /// 34 | public int ExpireMinutes { get; set; } = 60 * 24; // 默认1天 35 | 36 | /// 37 | /// 刷新令牌过期时间(分钟) 38 | /// 39 | public int RefreshExpireMinutes { get; set; } = 60 * 24 * 7; // 默认7天 40 | 41 | /// 42 | /// 获取签名凭证 43 | /// 44 | public SymmetricSecurityKey GetSymmetricSecurityKey() 45 | { 46 | return new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Secret)); 47 | } 48 | 49 | /// 50 | /// 初始化配置 51 | /// 52 | public static JwtOptions InitConfig(IConfiguration configuration) 53 | { 54 | var options = configuration.GetSection(Name).Get() ?? new JwtOptions(); 55 | 56 | // 如果配置中没有设置密钥,则生成一个随机密钥 57 | if (string.IsNullOrEmpty(options.Secret)) 58 | { 59 | options.Secret = Guid.NewGuid().ToString("N") + Guid.NewGuid().ToString("N"); 60 | } 61 | 62 | // 如果没有设置颁发者和接收者,则使用默认值 63 | if (string.IsNullOrEmpty(options.Issuer)) 64 | { 65 | options.Issuer = "KoalaWiki"; 66 | } 67 | 68 | if (string.IsNullOrEmpty(options.Audience)) 69 | { 70 | options.Audience = "KoalaWiki"; 71 | } 72 | 73 | return options; 74 | } 75 | } -------------------------------------------------------------------------------- /src/KoalaWiki/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/launchsettings.json", 3 | "profiles": { 4 | "http": { 5 | "commandName": "Project", 6 | "dotnetRunMessages": true, 7 | "launchBrowser": false, 8 | "applicationUrl": "http://localhost:5085", 9 | "environmentVariables": { 10 | "ASPNETCORE_ENVIRONMENT": "Development", 11 | "TASK_MAX_SIZE_PER_USER": "10" 12 | } 13 | }, 14 | "https": { 15 | "commandName": "Project", 16 | "dotnetRunMessages": true, 17 | "launchBrowser": false, 18 | "applicationUrl": "https://localhost:7065;http://localhost:5085", 19 | "environmentVariables": { 20 | "ASPNETCORE_ENVIRONMENT": "Development" 21 | } 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/KoalaWiki/Services/ChatShareMessageService.cs: -------------------------------------------------------------------------------- 1 | using FastService; 2 | using KoalaWiki.Core.DataAccess; 3 | using KoalaWiki.Domains; 4 | using KoalaWiki.Dto; 5 | using Microsoft.EntityFrameworkCore; 6 | 7 | namespace KoalaWiki.Services; 8 | 9 | public class ChatShareMessageService(IKoalaWikiContext koalaWikiContext) : FastApi 10 | { 11 | public async Task> GetListAsync(string chatShareMessageId, int page, 12 | int pageSize) 13 | { 14 | var chatMessage = koalaWikiContext.ChatShareMessages 15 | .AsNoTracking() 16 | .FirstOrDefault(x => x.Id == chatShareMessageId); 17 | 18 | var list = await koalaWikiContext.ChatShareMessageItems 19 | .AsNoTracking() 20 | .Where(x => x.ChatShareMessageId == chatShareMessageId) 21 | .OrderByDescending(x => x.CreatedAt) 22 | .Skip((page - 1) * pageSize) 23 | .Take(pageSize) 24 | .ToListAsync(); 25 | 26 | var total = await koalaWikiContext.ChatShareMessageItems 27 | .AsNoTracking() 28 | .CountAsync(x => x.Id == chatShareMessageId); 29 | 30 | return ResultDto.Success(new 31 | { 32 | items = list, 33 | total = total, 34 | info = chatMessage 35 | }); 36 | } 37 | 38 | public async Task> CreateAsync(ChatShareMessageInput input, HttpContext context) 39 | { 40 | // 获取ip 41 | var ip = context.Connection.RemoteIpAddress?.ToString(); 42 | if (context.Request.Headers.TryGetValue("X-Forwarded-For", out var forwardedFor)) 43 | { 44 | ip = forwardedFor.ToString(); 45 | } 46 | else if (context.Request.Headers.TryGetValue("X-Real-IP", out var realIp)) 47 | { 48 | ip = realIp.ToString(); 49 | } 50 | 51 | var warehouse = await koalaWikiContext.Warehouses 52 | .AsNoTracking() 53 | .FirstOrDefaultAsync(x => x.OrganizationName == input.Owner && x.Name == input.Name); 54 | 55 | var chatShareMessage = new ChatShareMessage 56 | { 57 | Ip = ip, 58 | Id = Guid.NewGuid().ToString(), 59 | WarehouseId = warehouse.Id, 60 | IsDeep = input.IsDeep, 61 | Question = input.Message, 62 | }; 63 | 64 | await koalaWikiContext.ChatShareMessages.AddAsync(chatShareMessage); 65 | 66 | await koalaWikiContext.SaveChangesAsync(); 67 | 68 | return ResultDto.Success(chatShareMessage.Id); 69 | } 70 | } -------------------------------------------------------------------------------- /src/KoalaWiki/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | }, 8 | "ConnectionStrings": { 9 | "Type": "sqlite",// postgres, mysql, sqlite, sqlserver 10 | "Default": "Data Source=KoalaWiki.db" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/KoalaWiki/plugins/CodeAnalysis/CodeDirSimplifier/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "schema": 1, 3 | "type": "completion", 4 | "description": "简化代码目录", 5 | "execution_settings": { 6 | "default": { 7 | "max_tokens": 8192, 8 | "temperature": 0.5 9 | }, 10 | "DeepSeek-V3": { 11 | "max_tokens": 16384, 12 | "temperature": 0.5 13 | }, 14 | "gpt-4.1-mini": { 15 | "max_tokens": 16384, 16 | "temperature": 0.5 17 | }, 18 | "gpt-4.1": { 19 | "max_tokens": 16384, 20 | "temperature": 0.5 21 | }, 22 | "gpt-4o": { 23 | "max_tokens": 16384, 24 | "temperature": 0.5 25 | }, 26 | "o4-mini": { 27 | "max_tokens": 16384 , 28 | "temperature": 0.5 29 | }, 30 | "o3-mini": { 31 | "max_tokens": 16384 , 32 | "temperature": 0.5 33 | }, 34 | "deepseek-chat": { 35 | "max_tokens": 8192 , 36 | "temperature": 0.5 37 | }, 38 | "gemini-2.5-pro-preview-05-06": { 39 | "max_tokens": 16384 , 40 | "temperature": 0.5 41 | }, 42 | "Qwen/Qwen3-235B-A22B": { 43 | "max_tokens": 16384 , 44 | "temperature": 0.5 45 | }, 46 | "grok-3": { 47 | "max_tokens": 16384 , 48 | "temperature": 0.5 49 | }, 50 | "qwen3-235b-a22b": { 51 | "max_tokens": 16384 , 52 | "temperature": 0.5 53 | }, 54 | "qwen2.5-coder-3b-instruct": { 55 | "max_tokens": 32768 , 56 | "temperature": 0.5 57 | } 58 | }, 59 | "input_variables": [ 60 | { 61 | "name": "code_files", 62 | "description": "代码文件列表", 63 | "required": true 64 | }, 65 | { 66 | "name": "readme", 67 | "description": "当前仓库文档", 68 | "required": true 69 | } 70 | ] 71 | } -------------------------------------------------------------------------------- /src/KoalaWiki/plugins/CodeAnalysis/CommitAnalyze/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "schema": 1, 3 | "type": "completion", 4 | "description": "git仓库提交记录分析", 5 | "execution_settings": { 6 | "default": { 7 | "max_tokens": 8192, 8 | "temperature": 0.5 9 | }, 10 | "DeepSeek-V3": { 11 | "max_tokens": 16384, 12 | "temperature": 0.5 13 | }, 14 | "gpt-4.1-mini": { 15 | "max_tokens": 16384, 16 | "temperature": 0.5 17 | }, 18 | "gpt-4.1": { 19 | "max_tokens": 16384, 20 | "temperature": 0.5 21 | }, 22 | "gpt-4o": { 23 | "max_tokens": 16384, 24 | "temperature": 0.5 25 | }, 26 | "o4-mini": { 27 | "max_tokens": 16384 , 28 | "temperature": 0.5 29 | }, 30 | "o3-mini": { 31 | "max_tokens": 16384 , 32 | "temperature": 0.5 33 | }, 34 | "deepseek-chat": { 35 | "max_tokens": 8192 , 36 | "temperature": 0.5 37 | }, 38 | "gemini-2.5-pro-preview-05-06": { 39 | "max_tokens": 16384 , 40 | "temperature": 0.5 41 | }, 42 | "Qwen/Qwen3-235B-A22B": { 43 | "max_tokens": 16384 , 44 | "temperature": 0.5 45 | }, 46 | "grok-3": { 47 | "max_tokens": 16384 , 48 | "temperature": 0.5 49 | }, 50 | "qwen3-235b-a22b": { 51 | "max_tokens": 16384 , 52 | "temperature": 0.5 53 | }, 54 | "qwen2.5-coder-3b-instruct": { 55 | "max_tokens": 32768 , 56 | "temperature": 0.5 57 | } 58 | }, 59 | "input_variables": [ 60 | { 61 | "name": "commit_message", 62 | "description": "Git提交记录", 63 | "required": true 64 | }, 65 | { 66 | "name": "git_repository", 67 | "description": "仓库地址", 68 | "required": true 69 | }, 70 | { 71 | "name": "readme", 72 | "description": "仓库默认文档内容", 73 | "required": true 74 | }, 75 | { 76 | "name": "branch", 77 | "description": "仓库分支", 78 | "required": true 79 | } 80 | ] 81 | } -------------------------------------------------------------------------------- /src/KoalaWiki/plugins/CodeAnalysis/FunctionPrompt/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "schema": 1, 3 | "type": "completion", 4 | "description": "git仓库提交记录分析", 5 | "execution_settings": { 6 | "default": { 7 | "max_tokens": 2048, 8 | "temperature": 0.3 9 | } 10 | }, 11 | "input_variables": [ 12 | { 13 | "name": "readme", 14 | "description": "仓库README文档", 15 | "required": true 16 | }, 17 | { 18 | "name": "owner", 19 | "description": "仓库所有者", 20 | "required": true 21 | }, 22 | { 23 | "name": "name", 24 | "description": "仓库名称", 25 | "required": true 26 | } 27 | ] 28 | } -------------------------------------------------------------------------------- /src/KoalaWiki/plugins/CodeAnalysis/FunctionPrompt/skprompt.txt: -------------------------------------------------------------------------------- 1 | You are an AI system designed to analyze GitHub repositories and create detailed descriptions for AI-powered assistants. Your task is to generate a comprehensive description of an AI assistant's capabilities based on the information provided about a given repository. 2 | 3 | First, review the README content and repository details: 4 | 5 | 6 | {{$readme}} 7 | 8 | 9 | {{$owner}} 10 | {{$name}} 11 | 12 | present the function description within tags. Write in first-person perspective, as if the AI assistant is describing its own capabilities. Include: 13 | 14 | - Detailed description of capabilities, explicitly linked to specific parts of the repository or README 15 | - Examples of how it can help with different aspects of the repository 16 | - Knowledge of key technologies or concepts 17 | - Unique features or aspects it can assist with 18 | - Comprehensive coverage of the entire repository 19 | 20 | Example structure (replace with actual content based on your analysis): 21 | 22 | 23 | I am an AI assistant specialized in [specific repository topic]. I can help you with [detailed list of capabilities based on the repository]. My knowledge encompasses [relevant technologies and concepts], allowing me to assist with tasks such as [specific examples of tasks]. I'm particularly adept at [unique feature or aspect of the repository]. Whether you need [example 1], [example 2], or [example 3], I'm here to provide detailed information and assistance based on the repository's content and structure. 24 | 25 | 26 | Remember to keep your analysis concise while ensuring the final description is as detailed and specific as possible, covering the entire repository. -------------------------------------------------------------------------------- /src/KoalaWiki/plugins/CodeAnalysis/GenerateDescription/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "schema": 1, 3 | "type": "completion", 4 | "description": "根据仓库目录结构,分析仓库的功能和结构,生成属于仓库的Readme", 5 | "execution_settings": { 6 | "default": { 7 | "max_tokens": 2048, 8 | "temperature": 0.8 9 | } 10 | }, 11 | "input_variables": [ 12 | { 13 | "name": "readme", 14 | "description": "仓库README", 15 | "required": true 16 | } 17 | ] 18 | } -------------------------------------------------------------------------------- /src/KoalaWiki/plugins/CodeAnalysis/GenerateDescription/skprompt.txt: -------------------------------------------------------------------------------- 1 | You are a GitHub repository expert tasked with analyzing a README file and generating a concise description for the repository. Here's the README content you need to analyze: 2 | 3 | 4 | {{$readme}} 5 | 6 | 7 | Your task is to generate a repository description that meets the following criteria: 8 | 1. The description should be no more than 100 characters long. 9 | 2. Focus on highlighting the core features and purpose of the repository. 10 | 3. Write in a style that is consistent with typical GitHub repository descriptions. 11 | 4. Ensure the description is clear, concise, and informative. 12 | 13 | Carefully analyze the README content, paying attention to the project's main features, purpose, and any standout characteristics. 14 | 15 | Based on your analysis, generate a concise description for the repository. Remember to adhere to the criteria mentioned above. 16 | 17 | Output your generated description within tags. Do not include any other text or explanations outside of these tags. -------------------------------------------------------------------------------- /src/KoalaWiki/plugins/CodeAnalysis/GenerateReadme/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "schema": 1, 3 | "type": "completion", 4 | "description": "根据仓库目录结构,分析仓库的功能和结构,生成属于仓库的Readme", 5 | "execution_settings": { 6 | "default": { 7 | "max_tokens": 8192, 8 | "temperature": 0.5 9 | }, 10 | "DeepSeek-V3": { 11 | "max_tokens": 16384, 12 | "temperature": 0.5 13 | }, 14 | "gpt-4.1-mini": { 15 | "max_tokens": 32768, 16 | "temperature": 0.5 17 | }, 18 | "gpt-4.1": { 19 | "max_tokens": 32768, 20 | "temperature": 0.5 21 | }, 22 | "gpt-4o": { 23 | "max_tokens": 16384, 24 | "temperature": 0.5 25 | }, 26 | "o4-mini": { 27 | "max_tokens": 100000 , 28 | "temperature": 0.5 29 | }, 30 | "o3-mini": { 31 | "max_tokens": 100000 , 32 | "temperature": 0.5 33 | }, 34 | "deepseek-chat": { 35 | "max_tokens": 8192 , 36 | "temperature": 0.5 37 | }, 38 | "gemini-2.5-pro-preview-05-06": { 39 | "max_tokens": 65536 , 40 | "temperature": 0.5 41 | }, 42 | "Qwen/Qwen3-235B-A22B": { 43 | "max_tokens": 32768 , 44 | "temperature": 0.5 45 | }, 46 | "grok-3": { 47 | "max_tokens": 65536 , 48 | "temperature": 0.5 49 | }, 50 | "qwen3-235b-a22b": { 51 | "max_tokens": 16384 , 52 | "temperature": 0.5 53 | }, 54 | "qwen2.5-coder-3b-instruct": { 55 | "max_tokens": 32768 , 56 | "temperature": 0.5 57 | } 58 | }, 59 | "input_variables": [ 60 | { 61 | "name": "catalogue", 62 | "description": "仓库目录结构", 63 | "required": true 64 | }, 65 | { 66 | "name": "branch", 67 | "description": "仓库分支", 68 | "required": true 69 | }, 70 | { 71 | "name": "git_repository", 72 | "description": "仓库地址", 73 | "required": true 74 | } 75 | ] 76 | } -------------------------------------------------------------------------------- /src/KoalaWiki/plugins/LanguagePromptFilter.cs: -------------------------------------------------------------------------------- 1 | using KoalaWiki.KoalaWarehouse; 2 | using Microsoft.SemanticKernel; 3 | 4 | namespace KoalaWiki.plugins; 5 | 6 | public class LanguagePromptFilter : IPromptRenderFilter 7 | { 8 | public async Task OnPromptRenderAsync(PromptRenderContext context, Func next) 9 | { 10 | await next(context); 11 | 12 | context.RenderedPrompt = "/no_think " + context.RenderedPrompt + Environment.NewLine + Prompt.Language; 13 | } 14 | } -------------------------------------------------------------------------------- /start-backend.bat: -------------------------------------------------------------------------------- 1 | set URLS=http://localhost:5085 2 | set KOALAWIKI_REPOSITORIES=/repositories 3 | REM 每个用户AI处理文档生成的最大并行数量 4 | SET TASK_MAX_SIZE_PER_USER=5 5 | REM 必须要支持function的模型 6 | SET CHAT_MODEL=DeepSeek-V3 7 | REM 分析模型,用于生成仓库目录结构 8 | SET ANALYSIS_MODEL= 9 | REM 您的APIkey 10 | SET CHAT_API_KEY= 11 | REM 设置生成语言默认为"中文" 12 | SET LANGUAGE= 13 | SET ENDPOINT=https://api.openai.com/v1 14 | SET DB_TYPE=sqlite 15 | REM 模型提供商,默认为OpenAI 支持AzureOpenAI和Anthropic 16 | SET MODEL_PROVIDER=OpenAI 17 | SET DB_CONNECTION_STRING=Data Source=./KoalaWiki.db 18 | REM 是否启用智能过滤,这可能影响AI得到仓库的文件目录 19 | SET EnableSmartFilter=true 20 | REM 仓库增量更新间隔,单位天 21 | SET UPDATE_INTERVAL=5 22 | REM 上传文件的最大限制,单位MB 23 | SET MAX_FILE_LIMIT=100 24 | REM 深度研究模型,为空使用CHAT_MODEL 25 | SET DEEP_RESEARCH_MODEL= 26 | REM 是否启用增量更新 27 | SET ENABLE_INCREMENTAL_UPDATE=true 28 | 29 | cd backend 30 | 31 | "KoalaWiki.exe" -------------------------------------------------------------------------------- /start-backend.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | export URLS=http://localhost:5085 4 | export KOALAWIKI_REPOSITORIES=/repositories 5 | # 每个用户AI处理文档生成的最大并行数量 6 | export TASK_MAX_SIZE_PER_USER=5 7 | # 必须要支持function的模型 8 | export CHAT_MODEL=DeepSeek-V3 9 | # 分析模型,用于生成仓库目录结构 10 | export ANALYSIS_MODEL= 11 | # 您的APIkey 12 | export CHAT_API_KEY= 13 | # 设置生成语言默认为"中文" 14 | export LANGUAGE= 15 | export ENDPOINT=https://api.openai.com/v1 16 | export DB_TYPE=sqlite 17 | # 模型提供商,默认为OpenAI 支持AzureOpenAI和Anthropic 18 | export MODEL_PROVIDER=OpenAI 19 | export DB_CONNECTION_STRING="Data Source=./KoalaWiki.db" 20 | # 是否启用智能过滤,这可能影响AI得到仓库的文件目录 21 | export EnableSmartFilter=true 22 | # 仓库增量更新间隔,单位天 23 | export UPDATE_INTERVAL=5 24 | # 上传文件的最大限制,单位MB 25 | export MAX_FILE_LIMIT=100 26 | # 深度研究模型,为空使用CHAT_MODEL 27 | export DEEP_RESEARCH_MODEL= 28 | # 是否启用增量更新 29 | export ENABLE_INCREMENTAL_UPDATE=true 30 | 31 | cd backend 32 | 33 | 34 | KoalaWiki -------------------------------------------------------------------------------- /start-frontend.bat: -------------------------------------------------------------------------------- 1 | cd ./frontend/standalone 2 | node server.js -------------------------------------------------------------------------------- /start-frontend.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cd ./frontend/standalone 4 | node server.js -------------------------------------------------------------------------------- /web/.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .git 3 | .gitignore 4 | Dockerfile 5 | dist 6 | .next 7 | .cache 8 | *.log 9 | *.md 10 | .env 11 | .env.local 12 | .env.development 13 | .env.production 14 | .env.test 15 | npm-debug.log* 16 | yarn-debug.log* 17 | yarn-error.log* -------------------------------------------------------------------------------- /web/.env.samples: -------------------------------------------------------------------------------- 1 | NEXT_PUBLIC_API_URL=http://localhost:5085 -------------------------------------------------------------------------------- /web/.npmrc: -------------------------------------------------------------------------------- 1 | registry=https://registry.npmmirror.com 2 | disturl=https://npmmirror.com/mirrors/node 3 | sass_binary_site=https://npmmirror.com/mirrors/node-sass/ 4 | phantomjs_cdnurl=https://npmmirror.com/mirrors/phantomjs/ 5 | electron_mirror=https://npmmirror.com/mirrors/electron/ 6 | chromedriver_cdnurl=https://npmmirror.com/mirrors/chromedriver 7 | operadriver_cdnurl=https://npmmirror.com/mirrors/operadriver 8 | selenium_cdnurl=https://npmmirror.com/mirrors/selenium 9 | node_inspector_cdnurl=https://npmmirror.com/mirrors/node-inspector 10 | fsevents_binary_host_mirror=https://npmmirror.com/mirrors/fsevents/ 11 | puppeteer_download_host=https://npmmirror.com/mirrors 12 | sentrycli_cdnurl=https://npmmirror.com/mirrors/sentry-cli -------------------------------------------------------------------------------- /web/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "i18n-ally.localesPaths": [ 3 | "public/locales", 4 | "app/i18n", 5 | "app/i18n/locales" 6 | ] 7 | } -------------------------------------------------------------------------------- /web/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM --platform=$BUILDPLATFORM node:23-alpine AS base 2 | 3 | # Install dependencies only when needed 4 | FROM base AS deps 5 | RUN apk add --no-cache libc6-compat 6 | WORKDIR /app 7 | 8 | COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml* .npmrc* ./ 9 | RUN \ 10 | if [ -f yarn.lock ]; then yarn --frozen-lockfile; \ 11 | elif [ -f package-lock.json ]; then npm ci; \ 12 | elif [ -f pnpm-lock.yaml ]; then corepack enable pnpm && pnpm i --frozen-lockfile; \ 13 | else echo "Lockfile not found." && exit 1; \ 14 | fi 15 | 16 | # Rebuild the source code only when needed 17 | FROM base AS builder 18 | WORKDIR /app 19 | COPY --from=deps /app/node_modules ./node_modules 20 | COPY . . 21 | 22 | RUN \ 23 | if [ -f yarn.lock ]; then yarn run build; \ 24 | elif [ -f package-lock.json ]; then npm run build; \ 25 | elif [ -f pnpm-lock.yaml ]; then corepack enable pnpm && pnpm run build; \ 26 | else echo "Lockfile not found." && exit 1; \ 27 | fi 28 | 29 | # Production image, copy all the files and run next 30 | FROM base AS runner 31 | WORKDIR /app 32 | 33 | ENV NODE_ENV=production 34 | 35 | RUN addgroup --system --gid 1001 nodejs 36 | RUN adduser --system --uid 1001 nextjs 37 | 38 | # 安装 sed 工具用于文本替换 39 | RUN apk add --no-cache sed 40 | 41 | COPY --from=builder /app/public ./public 42 | COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./ 43 | COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static 44 | 45 | # 复制启动脚本 46 | COPY start.sh /app/start.sh 47 | RUN chmod +x /app/start.sh 48 | 49 | # 改变所有权到 nextjs 用户 50 | RUN chown -R nextjs:nodejs /app 51 | 52 | USER nextjs 53 | 54 | EXPOSE 3000 55 | ENV PORT=3000 56 | ENV HOSTNAME="0.0.0.0" 57 | 58 | # 使用启动脚本而不是直接启动 server.js 59 | CMD ["/app/start.sh"] -------------------------------------------------------------------------------- /web/app/[owner]/[name]/ClientRepositoryPage.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | import { Suspense } from 'react'; 4 | import { RepositoryView } from './RepositoryView'; 5 | import { ServerLoadingErrorState } from '../../components/document/ServerComponents'; 6 | 7 | interface ClientRepositoryPageProps { 8 | owner: string; 9 | name: string; 10 | document: any; 11 | branch?: string; 12 | } 13 | 14 | export default function ClientRepositoryPage({ owner, name, document }: ClientRepositoryPageProps) { 15 | return ( 16 | }> 17 | 22 | 23 | ); 24 | } -------------------------------------------------------------------------------- /web/app/[owner]/[name]/RepositoryView.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | import { 4 | Row, 5 | Col, 6 | theme, 7 | } from 'antd'; 8 | import { useState, useMemo, useEffect } from 'react'; 9 | import { createAnchorItems, DocumentContent, DocumentSidebar, DocumentStyles, extractHeadings, MobileDocumentDrawer } from '../../components/document'; 10 | 11 | const { useToken } = theme; 12 | 13 | interface RepositoryViewProps { 14 | owner: string; 15 | name: string; 16 | document: any; 17 | } 18 | 19 | export function RepositoryView({ owner, name, document }: RepositoryViewProps) { 20 | const { token } = useToken(); 21 | const [headings, setHeadings] = useState<{key: string, title: string, level: number, id: string}[]>([]); 22 | 23 | const anchorItems = useMemo(() => { 24 | return createAnchorItems(headings); 25 | }, [headings]); 26 | 27 | useEffect(() => { 28 | // 提取标题作为目录 29 | if (document?.content) { 30 | const extractedHeadings = extractHeadings(document.content); 31 | setHeadings(extractedHeadings); 32 | } 33 | }, [document]); 34 | 35 | return ( 36 |
37 | 45 | 46 | 52 | 53 | 54 | 55 | 59 | 60 | 61 | 62 | 66 | 67 | 68 |
69 | ); 70 | } -------------------------------------------------------------------------------- /web/app/[owner]/[name]/changelog/page.tsx: -------------------------------------------------------------------------------- 1 | import { getChangeLog } from '../../../services/warehouseService'; 2 | import RepositoryInfo from '../RepositoryInfo'; 3 | 4 | // 服务器组件,处理数据获取 5 | export default async function ChangelogPage({ params, searchParams }: any) { 6 | try { 7 | const owner = params.owner; 8 | const name = params.name; 9 | // 从查询参数中获取分支信息 10 | const branch = searchParams.branch as string | undefined; 11 | 12 | if (!owner || !name) { 13 | throw new Error('Missing owner or repository name'); 14 | } 15 | 16 | // 在服务器端获取数据 17 | const response = await getChangeLog(owner, name, branch); 18 | 19 | // 如果获取数据失败,尝试从GitHub获取仓库信息 20 | if (!response.success || !response.data) { 21 | return ( 22 | 26 | ); 27 | } 28 | 29 | // 直接在服务器端渲染更新日志 30 | return ( 31 |
32 |

更新日志

33 |
34 |
35 | ); 36 | } catch (error) { 37 | console.error('Failed to load changelog:', error); 38 | const owner = params?.owner || ""; 39 | const name = params?.name || ""; 40 | 41 | // 出现错误时也展示GitHub仓库信息(如果有) 42 | return ( 43 | 47 | ); 48 | } 49 | } -------------------------------------------------------------------------------- /web/app/[owner]/[name]/layout.server.tsx: -------------------------------------------------------------------------------- 1 | import { documentCatalog } from '../../services/warehouseService'; 2 | import RepositoryLayoutClient from './layout.client'; 3 | 4 | export async function getRepositoryData(owner: string, name: string, branch?: string) { 5 | try { 6 | const { data } = await documentCatalog(owner, name, branch); 7 | return { 8 | catalogData: data || null, 9 | lastUpdated: data?.lastUpdate ?? "" 10 | }; 11 | } catch (error) { 12 | console.error('Failed to fetch document catalog:', error); 13 | return { 14 | catalogData: null, 15 | lastUpdated: '' 16 | }; 17 | } 18 | } 19 | 20 | export default async function RepositoryLayoutServer({ 21 | owner, 22 | name, 23 | children, 24 | branch 25 | }: { 26 | owner: string; 27 | name: string; 28 | children: React.ReactNode; 29 | branch: string; 30 | }) { 31 | const { catalogData, lastUpdated } = await getRepositoryData(owner, name, branch); 32 | 33 | // 确保initialCatalogData包含当前branch信息 34 | if (catalogData && branch) { 35 | catalogData.currentBranch = branch; 36 | } 37 | 38 | return ( 39 | 45 | {children} 46 | 47 | ); 48 | } -------------------------------------------------------------------------------- /web/app/[owner]/[name]/layout.tsx: -------------------------------------------------------------------------------- 1 | import { Metadata, ResolvingMetadata } from 'next'; 2 | import RepositoryLayoutServer from './layout.server'; 3 | 4 | type Props = { 5 | params: { owner: string; name: string; branch: string } 6 | children: React.ReactNode 7 | } 8 | 9 | // 为页面生成动态元数据 10 | export async function generateMetadata( 11 | { params }: Props, 12 | parent: ResolvingMetadata 13 | ): Promise { 14 | const { owner, name } = params; 15 | 16 | // 优化SEO的元数据 17 | return { 18 | title: `${name} - ${owner} 的文档仓库 | OpenDeekWiki`, 19 | description: `浏览 ${owner}/${name} 的文档内容,查看技术文档、API文档和其他相关资源。`, 20 | keywords: [`${name}`, `${owner}`, '文档', '知识库', 'OpenDeekWiki', '技术文档'], 21 | openGraph: { 22 | title: `${name} - ${owner} 的文档仓库`, 23 | description: `浏览 ${owner}/${name} 的文档内容,查看技术文档、API文档和其他相关资源。`, 24 | url: `/${owner}/${name}`, 25 | siteName: 'OpenDeekWiki', 26 | locale: 'zh_CN', 27 | type: 'website', 28 | }, 29 | twitter: { 30 | card: 'summary_large_image', 31 | title: `${name} - ${owner} 的文档仓库 | OpenDeekWiki`, 32 | description: `浏览 ${owner}/${name} 的文档内容,查看技术文档、API文档和其他相关资源。`, 33 | }, 34 | alternates: { 35 | canonical: `/${owner}/${name}`, 36 | }, 37 | robots: { 38 | index: true, 39 | follow: true, 40 | } 41 | } 42 | } 43 | 44 | export default async function RepositoryLayout({ 45 | params, 46 | children, 47 | }: Props) { 48 | return ( 49 | 54 | {children} 55 | 56 | ); 57 | } -------------------------------------------------------------------------------- /web/app/[owner]/[name]/page.tsx: -------------------------------------------------------------------------------- 1 | import { getWarehouseOverview } from '../../services'; 2 | import { ServerLoadingErrorState } from '../../components/document/ServerComponents'; 3 | import ClientRepositoryPage from './ClientRepositoryPage'; 4 | import RepositoryInfo from './RepositoryInfo'; 5 | import { checkGitHubRepoExists } from '../../services/githubService'; 6 | 7 | // 服务器组件,处理数据获取 8 | export default async function RepositoryPage({ params, searchParams }: any) { 9 | try { 10 | const owner = params.owner; 11 | const name = params.name; 12 | // 从查询参数中获取分支信息 13 | const branch = searchParams.branch as string | undefined; 14 | 15 | if (!owner || !name) { 16 | throw new Error('Missing owner or repository name'); 17 | } 18 | 19 | // 在服务器端获取数据 20 | const response = await getWarehouseOverview(owner, name, branch); 21 | 22 | // 如果获取数据失败,尝试从GitHub获取仓库信息 23 | if (!response.success || !response.data) { 24 | // 检查GitHub仓库是否存在 25 | const githubRepoExists = await checkGitHubRepoExists(owner, name, branch); 26 | 27 | // 如果GitHub仓库存在,则显示GitHub仓库信息 28 | if (githubRepoExists) { 29 | return ( 30 | 35 | ); 36 | } else { 37 | // 如果GitHub仓库也不存在,则显示添加仓库提示 38 | return ( 39 | 44 | ); 45 | } 46 | } 47 | 48 | // 将数据传递给客户端组件进行渲染 49 | return ( 50 | 55 | ); 56 | } catch (error) { 57 | const owner = params?.owner || ""; 58 | const name = params?.name || ""; 59 | const branch = searchParams?.branch as string | undefined; 60 | 61 | // 出现错误时也展示GitHub仓库信息(如果有) 62 | return ( 63 | 68 | ); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /web/app/admin/page.tsx: -------------------------------------------------------------------------------- 1 | import Dashboard from './pages/Dashboard'; 2 | 3 | export default function AdminPage() { 4 | return ; 5 | } -------------------------------------------------------------------------------- /web/app/admin/styles.module.css: -------------------------------------------------------------------------------- 1 | .adminLayout { 2 | display: flex; 3 | height: 100vh; 4 | background-color: #f7f9fc; 5 | } 6 | 7 | .sidebarContainer { 8 | position: fixed; 9 | left: 0; 10 | top: 0; 11 | height: 100%; 12 | background-color: white; 13 | transition: all 0.3s; 14 | box-shadow: 0 1px 4px rgba(0, 0, 0, 0.05); 15 | z-index: 20; 16 | } 17 | 18 | .sidebarOpen { 19 | width: 16rem; /* 64px */ 20 | } 21 | 22 | .sidebarClosed { 23 | width: 5rem; /* 20px */ 24 | } 25 | 26 | .sidebarLogo { 27 | height: 4rem; 28 | display: flex; 29 | align-items: center; 30 | justify-content: center; 31 | padding: 0 1rem; 32 | border-bottom: 1px solid #f1f5f9; 33 | } 34 | 35 | .mainContent { 36 | display: flex; 37 | flex-direction: column; 38 | flex: 1; 39 | transition: all 0.3s; 40 | } 41 | 42 | .mainContentSidebarClosed { 43 | margin-left: 5rem; 44 | } 45 | 46 | .headerContainer { 47 | background-color: white; 48 | height: 4rem; 49 | border-bottom: 1px solid #f1f5f9; 50 | display: flex; 51 | align-items: center; 52 | padding: 0 1.5rem; 53 | position: sticky; 54 | top: 0; 55 | z-index: 10; 56 | box-shadow: 0 1px 2px rgba(0, 0, 0, 0.03); 57 | } 58 | 59 | .contentContainer { 60 | flex: 1; 61 | overflow: auto; 62 | padding: 1.5rem; 63 | } 64 | 65 | .contentWrapper { 66 | background-color: white; 67 | border-radius: 0.5rem; 68 | box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05); 69 | padding: 1.5rem; 70 | min-height: calc(100vh - 8rem); 71 | } 72 | 73 | .navItem { 74 | display: flex; 75 | align-items: center; 76 | padding: 0.75rem 1rem; 77 | border-radius: 0.5rem; 78 | transition: all 0.2s; 79 | margin-bottom: 0.5rem; 80 | color: #4b5563; 81 | text-decoration: none; 82 | } 83 | 84 | .navItem:hover { 85 | background-color: #f3f4f6; 86 | } 87 | 88 | .navItemActive { 89 | background-color: #f0f7ff !important; 90 | color: #0771c9; 91 | font-weight: 500; 92 | } 93 | 94 | .navItemIcon { 95 | font-size: 1.25rem; 96 | } 97 | 98 | .navItemLabel { 99 | margin-left: 0.75rem; 100 | } 101 | 102 | /* 使用group类悬停支持 */ 103 | .group:hover .group-hover\:block { 104 | display: block !important; 105 | } 106 | 107 | .group:hover .group-hover\:rotate-90 { 108 | transform: rotate(90deg); 109 | } 110 | 111 | @media (min-width: 768px) { 112 | .sidebarContainer { 113 | position: relative; 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /web/app/admin/test-auth.md: -------------------------------------------------------------------------------- 1 | # 管理后台认证测试说明 2 | 3 | ## 功能测试步骤 4 | 5 | ### 1. 设置登录状态 6 | 7 | 在浏览器控制台中执行以下代码来模拟登录: 8 | 9 | ```javascript 10 | // 设置管理员登录 11 | localStorage.setItem('userToken', 'admin-token-123'); 12 | localStorage.setItem('userName', '超级管理员'); 13 | localStorage.setItem('userInfo', JSON.stringify({ 14 | role: 'admin', 15 | name: '超级管理员', 16 | id: '1' 17 | })); 18 | 19 | // 刷新页面 20 | location.reload(); 21 | ``` 22 | 23 | ### 2. 测试管理员权限 24 | 25 | 管理员应该能看到所有菜单项: 26 | - 数据统计 27 | - 用户管理 28 | - 仓库管理 29 | - 微调数据 30 | - 系统管理 31 | 32 | ### 3. 测试普通用户权限 33 | 34 | ```javascript 35 | // 设置普通用户登录 36 | localStorage.setItem('userToken', 'user-token-456'); 37 | localStorage.setItem('userName', '普通用户'); 38 | localStorage.setItem('userInfo', JSON.stringify({ 39 | role: 'user', 40 | name: '普通用户', 41 | id: '2' 42 | })); 43 | 44 | // 刷新页面 45 | location.reload(); 46 | ``` 47 | 48 | 普通用户只能看到受限菜单: 49 | - 数据统计 50 | - 微调数据 51 | 52 | ### 4. 测试退出登录功能 53 | 54 | 1. 点击右上角的用户头像 55 | 2. 在下拉菜单中点击"退出登录" 56 | 3. 验证: 57 | - 页面跳转到登录页面 58 | - localStorage中的认证信息被清除 59 | - 控制台输出退出日志 60 | 61 | ### 5. 验证清除的数据 62 | 63 | 退出登录后,在控制台检查: 64 | 65 | ```javascript 66 | // 这些应该都返回null 67 | console.log('userToken:', localStorage.getItem('userToken')); 68 | console.log('userName:', localStorage.getItem('userName')); 69 | console.log('userInfo:', localStorage.getItem('userInfo')); 70 | console.log('redirectPath:', localStorage.getItem('redirectPath')); 71 | ``` 72 | 73 | ## 功能特性 74 | 75 | ### 下拉菜单交互 76 | - 点击头像区域显示/隐藏下拉菜单 77 | - 点击空白处自动关闭下拉菜单 78 | - 菜单项有hover效果 79 | - 退出登录按钮使用红色突出显示 80 | 81 | ### 角色权限控制 82 | - admin:完整菜单权限 83 | - 其他角色:受限菜单权限 84 | - 控制台输出调试信息 85 | 86 | ### 安全清理 87 | - 退出时清除所有用户相关数据 88 | - 重置组件状态 89 | - 自动跳转到登录页面 -------------------------------------------------------------------------------- /web/app/components/DocumentToc.tsx: -------------------------------------------------------------------------------- 1 | import { Anchor, Card, Divider, Typography } from 'antd'; 2 | import { AnchorLinkItemProps } from 'antd/es/anchor/Anchor'; 3 | import { UnorderedListOutlined } from '@ant-design/icons'; 4 | 5 | const { Title } = Typography; 6 | 7 | interface DocumentTocProps { 8 | toc: AnchorLinkItemProps[]; 9 | } 10 | 11 | const DocumentToc: React.FC = ({ toc }) => { 12 | if (!toc || toc.length === 0) { 13 | return null; 14 | } 15 | 16 | return ( 17 | 21 | 22 | 文档目录 23 |
24 | } 25 | > 26 | 27 | 34 | 35 | ); 36 | }; 37 | 38 | export default DocumentToc; -------------------------------------------------------------------------------- /web/app/components/RepositoryList.tsx: -------------------------------------------------------------------------------- 1 | import { Col, Empty, Row } from 'antd'; 2 | import { Repository } from '../types'; 3 | import RepositoryCard from './RepositoryCard'; 4 | import { useTranslation } from '../i18n/client'; 5 | 6 | interface RepositoryListProps { 7 | repositories: Repository[]; 8 | } 9 | 10 | const RepositoryList: React.FC = ({ repositories }) => { 11 | const { t } = useTranslation(); 12 | 13 | if (!repositories.length) { 14 | return ; 15 | } 16 | 17 | return ( 18 |
19 | 20 | {repositories.map((repository) => ( 21 | 22 | 23 | 24 | ))} 25 | 26 |
27 | ); 28 | }; 29 | 30 | export default RepositoryList; -------------------------------------------------------------------------------- /web/app/components/document/AntThinking.tsx: -------------------------------------------------------------------------------- 1 | import Component from './Component'; 2 | import rehypePlugin from './rehypePlugin'; 3 | 4 | const AntThinkingElement = { 5 | Component, 6 | rehypePlugin, 7 | tag: 'antThinking', 8 | }; 9 | 10 | export default AntThinkingElement; -------------------------------------------------------------------------------- /web/app/components/document/DocumentHeader.tsx: -------------------------------------------------------------------------------- 1 | import React, { ReactNode } from 'react'; 2 | import { Card, Typography, Breadcrumb, Tag, Divider, Space } from 'antd'; 3 | import { ClockCircleOutlined } from '@ant-design/icons'; 4 | import Link from 'next/link'; 5 | 6 | const { Title, Text } = Typography; 7 | 8 | interface DocumentHeaderProps { 9 | document: any; 10 | lastUpdated: string; 11 | breadcrumbItems: {title: ReactNode}[]; 12 | token: any; 13 | } 14 | 15 | const DocumentHeader: React.FC = ({ 16 | document, 17 | lastUpdated, 18 | breadcrumbItems, 19 | token 20 | }) => { 21 | return ( 22 | <> 23 | 24 | 25 | 26 | 27 | {document && ( 28 |
34 | 42 | {document.title} 43 | 44 | 45 |
46 | 47 | 文档 48 |
49 | 50 | 51 |
52 | )} 53 | 54 | ); 55 | }; 56 | 57 | export default DocumentHeader; -------------------------------------------------------------------------------- /web/app/components/document/MarkdownElements.ts: -------------------------------------------------------------------------------- 1 | import AntThinkingElement from './AntThinking'; 2 | 3 | export const markdownElements = [ AntThinkingElement]; -------------------------------------------------------------------------------- /web/app/components/document/index.ts: -------------------------------------------------------------------------------- 1 | export { default as DocumentHeader } from './DocumentHeader'; 2 | export { default as DocumentContent } from './DocumentContent'; 3 | export { default as DocumentSidebar } from './DocumentSidebar'; 4 | export { default as MobileDocumentDrawer } from './MobileDocumentDrawer'; 5 | export { default as LoadingErrorState } from './LoadingErrorState'; 6 | export { default as DocumentStyles } from './DocumentStyles'; 7 | export { default as ChangelogContent } from './ChangelogContent'; 8 | export { default as SourceFiles } from './SourceFiles'; 9 | 10 | // 导出服务器组件 11 | export * from './ServerComponents'; 12 | 13 | // 工具函数导出 14 | export * from './utils/headingUtils'; 15 | export * from './utils/mermaidUtils'; -------------------------------------------------------------------------------- /web/app/components/document/rehypePlugin.ts: -------------------------------------------------------------------------------- 1 | import type { Node } from 'unist'; 2 | import { visit } from 'unist-util-visit'; 3 | 4 | // eslint-disable-next-line unicorn/consistent-function-scoping 5 | const rehypePlugin = () => (tree: Node) => { 6 | visit(tree, 'element', (node: any, index, parent) => { 7 | if (node.type === 'element' && node.tagName === 'p') { 8 | console.log(node); 9 | const children = node.children || []; 10 | const openTagIndex = children.findIndex( 11 | (child: any) => child.type === 'raw' && child.value === '', 12 | ); 13 | const closeTagIndex = children.findIndex( 14 | (child: any) => child.type === 'raw' && child.value === '', 15 | ); 16 | 17 | if (openTagIndex !== -1 && closeTagIndex !== -1 && closeTagIndex > openTagIndex) { 18 | const content = children.slice(openTagIndex + 1, closeTagIndex); 19 | const antThinkingNode = { 20 | children: content, 21 | properties: {}, 22 | tagName: 'antThinking', 23 | type: 'element', 24 | }; 25 | 26 | // Replace the entire paragraph with our new antThinking node 27 | parent.children.splice(index, 1, antThinkingNode); 28 | return index; // Skip processing the newly inserted node 29 | } 30 | } 31 | }); 32 | }; 33 | 34 | export default rehypePlugin; -------------------------------------------------------------------------------- /web/app/components/document/thinking/remarkPlugin.ts: -------------------------------------------------------------------------------- 1 | import { toMarkdown } from 'mdast-util-to-markdown'; 2 | import { SKIP, visit } from 'unist-util-visit'; 3 | 4 | // 预处理函数:确保 think 标签前后有两个换行符 5 | export const normalizeThinkTags = (markdown: string) => { 6 | // 删除```mermaid里面的[]里面的(),保留[] 7 | return markdown 8 | // 处理mermaid代码块中的[]()格式 9 | // 确保 标签前后有两个换行符 10 | .replaceAll(/([^\n])\s*/g, '$1\n\n') 11 | .replaceAll(/\s*([^\n])/g, '\n\n$1') 12 | // 确保 标签前后有两个换行符 13 | .replaceAll(/([^\n])\s*<\/think>/g, '$1\n\n') 14 | .replaceAll(/<\/think>\s*([^\n])/g, '\n\n$1') 15 | // 处理可能产生的多余换行符 16 | .replaceAll(/\n{3,}/g, '\n\n') 17 | }; 18 | 19 | export const remarkCaptureThink = () => { 20 | return (tree: any) => { 21 | visit(tree, 'html', (node, index, parent) => { 22 | if (node.value === '') { 23 | const startIndex = index as number; 24 | let endIndex = startIndex + 1; 25 | let hasCloseTag = false; 26 | 27 | // 查找闭合标签 28 | while (endIndex < parent.children.length) { 29 | const sibling = parent.children[endIndex]; 30 | if (sibling.type === 'html' && sibling.value === '') { 31 | hasCloseTag = true; 32 | break; 33 | } 34 | endIndex++; 35 | } 36 | 37 | // 计算需要删除的节点范围 38 | const deleteCount = hasCloseTag 39 | ? endIndex - startIndex + 1 40 | : parent.children.length - startIndex; 41 | 42 | // 提取内容节点 43 | const contentNodes = parent.children.slice( 44 | startIndex + 1, 45 | hasCloseTag ? endIndex : undefined, 46 | ); 47 | 48 | // 转换为 Markdown 字符串 49 | const content = contentNodes 50 | .map((n: any) => toMarkdown(n)) 51 | .join('\n\n') 52 | .trim(); 53 | 54 | // 创建自定义节点 55 | const thinkNode = { 56 | data: { 57 | hChildren: [{ type: 'text', value: content }], 58 | hName: 'think', 59 | }, 60 | position: node.position, 61 | type: 'thinkBlock', 62 | }; 63 | 64 | // 替换原始节点 65 | parent.children.splice(startIndex, deleteCount, thinkNode); 66 | 67 | // 跳过已处理的节点 68 | return [SKIP, startIndex + 1]; 69 | } 70 | }); 71 | }; 72 | }; -------------------------------------------------------------------------------- /web/app/components/document/utils/headingUtils.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 从Markdown文本中提取标题信息 3 | * @param markdown Markdown文本内容 4 | * @returns 提取的标题数组 5 | */ 6 | export const extractHeadings = (markdown: string): {key: string, title: string, level: number, id: string}[] => { 7 | const headingRegex = /^(#{1,6})\s+(.+)$/gm; 8 | const matches = Array.from(markdown.matchAll(headingRegex)); 9 | 10 | return matches.map((match, index) => { 11 | const level = match[1].length; 12 | const title = match[2]; 13 | const key = `heading-${index}`; 14 | // 生成ID,用于锚点定位 15 | const id = title.toLowerCase().replace(/\s+/g, '-').replace(/[^\w\u4e00-\u9fa5-]/g, ''); 16 | 17 | return { key, title, level, id }; 18 | }); 19 | }; 20 | 21 | /** 22 | * 将平面标题列表转换为嵌套的锚点项目结构 23 | * @param headings 标题数组 24 | * @returns 组织后的锚点项目数组 25 | */ 26 | export const createAnchorItems = (headings: {key: string, title: string, level: number, id: string}[]) => { 27 | if (!headings.length) return []; 28 | 29 | // 创建嵌套结构的目录 30 | const result: any[] = []; 31 | const levels: any[] = [{ children: result }]; 32 | 33 | headings.forEach(heading => { 34 | const item = { 35 | key: heading.key, 36 | href: `#${heading.id}`, 37 | title: heading.title, 38 | children: [], 39 | }; 40 | 41 | // 查找适当的父级 42 | while (levels.length > 1 && levels[levels.length - 1].level >= heading.level) { 43 | levels.pop(); 44 | } 45 | 46 | // 将当前项添加到父级 47 | levels[levels.length - 1].children.push(item); 48 | 49 | // 将当前项添加到级别堆栈 50 | if (heading.level < 4) { // 只对 h1-h3 建立嵌套结构 51 | levels.push({ level: heading.level, children: item.children }); 52 | } 53 | }); 54 | 55 | return result; 56 | }; -------------------------------------------------------------------------------- /web/app/components/document/utils/mermaidUtils.ts: -------------------------------------------------------------------------------- 1 | import mermaid from 'mermaid'; 2 | 3 | /** 4 | * 初始化Mermaid图表配置 5 | * @param isDarkMode 是否为暗黑模式 6 | */ 7 | export const initializeMermaid = (isDarkMode: boolean) => { 8 | mermaid.initialize({ 9 | startOnLoad: false, // 手动控制渲染 10 | theme: isDarkMode ? 'dark' : 'default', 11 | securityLevel: 'loose', 12 | flowchart: { 13 | htmlLabels: true, 14 | curve: 'basis', 15 | useMaxWidth: true 16 | }, 17 | sequence: { 18 | showSequenceNumbers: true, 19 | actorMargin: 80, 20 | useMaxWidth: true 21 | }, 22 | gantt: { 23 | axisFormat: '%Y-%m-%d', 24 | titleTopMargin: 25, 25 | barHeight: 20, 26 | barGap: 4 27 | }, 28 | er: { 29 | useMaxWidth: true 30 | }, 31 | journey: { 32 | useMaxWidth: true 33 | }, 34 | pie: { 35 | useMaxWidth: true 36 | } 37 | }); 38 | }; 39 | 40 | /** 41 | * 渲染页面上的所有Mermaid图表 42 | * @returns 一个Promise,表示渲染操作的完成 43 | */ 44 | export const renderMermaidDiagrams = async (): Promise => { 45 | try { 46 | // 给渲染一些时间以确保DOM已更新 47 | await new Promise(resolve => setTimeout(resolve, 300)); 48 | 49 | // 查找所有mermaid图表并渲染 50 | const diagrams = document.querySelectorAll('.mermaid'); 51 | if (diagrams.length > 0) { 52 | await mermaid.run({ 53 | querySelector: '.mermaid', 54 | }); 55 | return true; 56 | } 57 | return false; 58 | } catch (error) { 59 | console.error('渲染图表时出错:', error); 60 | return false; 61 | } 62 | }; -------------------------------------------------------------------------------- /web/app/const/urlconst.ts: -------------------------------------------------------------------------------- 1 | import pkg from '../../package.json' 2 | 3 | const homepage = pkg.homepage; 4 | 5 | export { 6 | homepage 7 | } -------------------------------------------------------------------------------- /web/app/i18n/client.ts: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import { useEffect } from 'react'; 4 | import i18next from 'i18next'; 5 | import { initReactI18next, useTranslation as useTranslationOrg } from 'react-i18next'; 6 | import resourcesToBackend from 'i18next-resources-to-backend'; 7 | import LanguageDetector from 'i18next-browser-languagedetector'; 8 | import { languages, getOptions } from './settings'; 9 | 10 | // 确保i18next只初始化一次 11 | const i18nextInstance = i18next 12 | .use(initReactI18next) 13 | .use(LanguageDetector) 14 | .use(resourcesToBackend((language: string, namespace: string) => 15 | import(`../../public/locales/${language}/${namespace}.json`))); 16 | 17 | // 检查i18next是否已经初始化 18 | if (!i18next.isInitialized) { 19 | // @ts-ignore - i18next类型定义与实际使用有差异 20 | i18nextInstance.init({ 21 | ...getOptions(), 22 | detection: { 23 | order: ['querystring', 'cookie', 'navigator'], 24 | lookupQuerystring: 'locale', 25 | caches: ['cookie'], 26 | cookieExpirationDate: new Date(Date.now() + 1000 * 60 * 60 * 24 * 365), // 1 year 27 | }, 28 | react: { 29 | useSuspense: false, // 禁用Suspense以避免hydration问题 30 | } 31 | }); 32 | } 33 | 34 | export function useTranslation(ns: string | string[] = 'common', options = {}) { 35 | const ret = useTranslationOrg(ns, options); 36 | const { i18n } = ret; 37 | 38 | useEffect(() => { 39 | languages.forEach((lng) => { 40 | const namespaces = Array.isArray(ns) ? ns : [ns]; 41 | namespaces.forEach((namespace) => { 42 | i18n.loadNamespaces(namespace); 43 | }); 44 | }); 45 | }, [i18n, ns]); 46 | 47 | return ret; 48 | } -------------------------------------------------------------------------------- /web/app/i18n/server.ts: -------------------------------------------------------------------------------- 1 | import { createInstance } from 'i18next'; 2 | import resourcesToBackend from 'i18next-resources-to-backend'; 3 | import { initReactI18next } from 'react-i18next/initReactI18next'; 4 | import { getOptions } from './settings'; 5 | 6 | const initI18next = async (lng: string, ns: string) => { 7 | const i18nInstance = createInstance(); 8 | await i18nInstance 9 | .use(initReactI18next) 10 | .use(resourcesToBackend((language: string, namespace: string) => 11 | import(`../../public/locales/${language}/${namespace}.json`))) 12 | .init(getOptions(lng, ns)); 13 | 14 | return i18nInstance; 15 | }; 16 | 17 | export async function getTranslation(lng: string, ns: string, options: any = {}) { 18 | const i18nextInstance = await initI18next(lng, ns); 19 | return { 20 | t: i18nextInstance.getFixedT(lng, ns, options.keyPrefix), 21 | i18n: i18nextInstance 22 | }; 23 | } -------------------------------------------------------------------------------- /web/app/i18n/settings.ts: -------------------------------------------------------------------------------- 1 | export const fallbackLng = 'zh-CN'; 2 | export const defaultNS = 'common'; 3 | 4 | export const languages = [ 5 | 'zh-CN', // 中文(简体) 6 | 'en-US', // 英文(美国) 7 | 'zh-TW', // 中文(繁体) 8 | 'ja', // 日语 9 | 'ko', // 韩语 10 | 'de', // 德语 11 | 'fr', // 法语 12 | 'es', // 西班牙语 13 | 'it', // 意大利语 14 | 'pt', // 葡萄牙语 15 | 'ru', // 俄语 16 | 'ar', // 阿拉伯语 17 | 'hi', // 印地语 18 | 'nl', // 荷兰语 19 | 'tr', // 土耳其语 20 | 'vi', // 越南语 21 | 'id', // 印尼语 22 | 'th' // 泰语 23 | ]; 24 | 25 | export function getOptions(lng = fallbackLng, ns = defaultNS) { 26 | return { 27 | supportedLngs: languages, 28 | fallbackLng, 29 | lng, 30 | fallbackNS: defaultNS, 31 | defaultNS, 32 | ns 33 | }; 34 | } -------------------------------------------------------------------------------- /web/app/login/auth.module.css: -------------------------------------------------------------------------------- 1 | .authContainer { 2 | display: flex; 3 | justify-content: center; 4 | align-items: center; 5 | min-height: 100vh; 6 | background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%); 7 | padding: 20px; 8 | } 9 | 10 | .authWrapper { 11 | width: 100%; 12 | max-width: 420px; 13 | animation: fadeIn 0.5s ease-out; 14 | } 15 | 16 | .authCard { 17 | border-radius: 12px; 18 | box-shadow: 0 8px 24px rgba(0, 0, 0, 0.1); 19 | overflow: hidden; 20 | } 21 | 22 | .authHeader { 23 | text-align: center; 24 | margin-bottom: 24px; 25 | } 26 | 27 | .authTitle { 28 | font-size: 24px; 29 | margin-bottom: 8px; 30 | font-weight: 600; 31 | color: #1677ff; 32 | } 33 | 34 | .authSubtitle { 35 | color: #8c8c8c; 36 | font-size: 14px; 37 | } 38 | 39 | .authForm { 40 | margin-top: 16px; 41 | } 42 | 43 | .rememberForgot { 44 | display: flex; 45 | justify-content: space-between; 46 | align-items: center; 47 | } 48 | 49 | .forgotLink { 50 | color: #1677ff; 51 | font-size: 14px; 52 | } 53 | 54 | .loginButton { 55 | height: 44px; 56 | font-size: 16px; 57 | border-radius: 6px; 58 | } 59 | 60 | .registerLink { 61 | text-align: center; 62 | margin: 16px 0; 63 | color: #8c8c8c; 64 | } 65 | 66 | .socialLogin { 67 | display: flex; 68 | justify-content: center; 69 | gap: 16px; 70 | margin-top: 16px; 71 | } 72 | 73 | .socialButton { 74 | border-radius: 6px; 75 | display: flex; 76 | align-items: center; 77 | justify-content: center; 78 | width: 120px; 79 | } 80 | 81 | .socialButton:hover { 82 | background-color: #f0f0f0; 83 | border-color: #d9d9d9; 84 | } 85 | 86 | .siteFormItemIcon { 87 | color: #bfbfbf; 88 | } 89 | 90 | /* 注册页面特定样式 */ 91 | .agreement { 92 | margin-bottom: 24px; 93 | } 94 | 95 | .agreementText { 96 | color: #8c8c8c; 97 | font-size: 14px; 98 | } 99 | 100 | /* 动画效果 */ 101 | @keyframes fadeIn { 102 | from { 103 | opacity: 0; 104 | transform: translateY(20px); 105 | } 106 | to { 107 | opacity: 1; 108 | transform: translateY(0); 109 | } 110 | } -------------------------------------------------------------------------------- /web/app/page.module.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AIDotNet/OpenDeepWiki/ee597a3390e5b84bbd82b9066ac68aae6104cbbb/web/app/page.module.css -------------------------------------------------------------------------------- /web/app/page.tsx: -------------------------------------------------------------------------------- 1 | import { getWarehouse } from './services/warehouseService'; 2 | import { getBasicHomeStats } from './services/statsService'; 3 | import HomeClient from './components/HomeClient'; 4 | import { Suspense } from 'react'; 5 | import { Spin } from 'antd'; 6 | 7 | export default async function Home({ searchParams = {} }: any) { 8 | // 确保 searchParams 已经被解析 9 | const resolvedSearchParams = await searchParams; 10 | 11 | // 从 URL 参数中获取分页信息 12 | const page = Number(resolvedSearchParams?.page) || 1; 13 | const pageSize = Number(resolvedSearchParams?.pageSize) || 20; 14 | const keyword = resolvedSearchParams?.keyword || ''; 15 | 16 | // 并行获取初始数据和统计数据 17 | const [response, statsData] = await Promise.all([ 18 | getWarehouse(page, pageSize, keyword), 19 | getBasicHomeStats() 20 | ]); 21 | 22 | const initialRepositories = response.success ? response.data.items : []; 23 | const initialTotal = response.success ? response.data.total : 0; 24 | 25 | return ( 26 | }> 27 | 35 | 36 | ); 37 | } -------------------------------------------------------------------------------- /web/app/services/chatShareMessageServce.ts: -------------------------------------------------------------------------------- 1 | import { API_URL, fetchApi } from './api'; 2 | 3 | interface ChatShareMessageInput { 4 | isDeep: boolean; 5 | owner: string; 6 | name: string; 7 | message: string; 8 | branch?: string; 9 | } 10 | /** 11 | * Submit a new repository to the warehouse 12 | * 这个函数仍然需要在客户端使用 13 | */ 14 | export async function createChatShareMessage( 15 | data: ChatShareMessageInput 16 | ): Promise { 17 | return await fetchApi(API_URL + '/api/ChatShareMessage', { 18 | method: 'POST', 19 | body: JSON.stringify(data), 20 | }); 21 | } 22 | 23 | /** 24 | * 25 | * @param chatShareMessageId 26 | * @param page 27 | * @param pageSize 28 | * @returns 29 | */ 30 | export async function getChatShareMessageList(chatShareMessageId:string,page:number,pageSize:number){ 31 | return await fetchApi(API_URL +`/api/ChatShareMessage/List?chatShareMessageId=${chatShareMessageId}&page=${page}&pageSize=${pageSize}`) 32 | } -------------------------------------------------------------------------------- /web/app/services/fetchApi.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * API响应接口 3 | */ 4 | export interface ApiResponse { 5 | code: number; 6 | message?: string; 7 | data: T; 8 | } 9 | 10 | /** 11 | * 封装的fetch函数,用于API请求 12 | * @param url 请求地址 13 | * @param options 请求选项 14 | * @returns API响应 15 | */ 16 | export async function fetchApi( 17 | url: string, 18 | options: RequestInit = {} 19 | ): Promise> { 20 | // 设置默认请求头 21 | const headers = { 22 | 'Content-Type': 'application/json', 23 | ...options.headers, 24 | }; 25 | 26 | // 获取token(如果存在) 27 | const token = typeof window !== 'undefined' ? localStorage.getItem('userToken') : null; 28 | if (token) { 29 | headers['Authorization'] = `Bearer ${token}`; 30 | } 31 | try { 32 | // 创建 AbortController 用于超时控制 33 | const controller = new AbortController(); 34 | const timeoutId = setTimeout(() => controller.abort(), 5 * 60 * 1000); // 5分钟超时 35 | 36 | const response = await fetch(url, { 37 | ...options, 38 | headers, 39 | signal: controller.signal, 40 | }); 41 | 42 | clearTimeout(timeoutId); // 清除超时定时器 43 | 44 | // 如果响应不成功,抛出错误 45 | if (!response.ok) { 46 | if (response.status === 401) { 47 | // 未授权,清除token并重定向到登录页 48 | if (typeof window !== 'undefined') { 49 | localStorage.removeItem('userToken'); 50 | localStorage.removeItem('refreshToken'); 51 | localStorage.removeItem('userInfo'); 52 | // 保存当前路径用于登录后重定向 53 | localStorage.setItem('redirectPath', window.location.pathname); 54 | window.location.href = '/login'; 55 | } 56 | } 57 | 58 | const errorData = await response.json(); 59 | throw new Error(errorData.message || '请求失败'); 60 | } 61 | 62 | // 解析响应数据 63 | const data = await response.json(); 64 | return data; 65 | } catch (error) { 66 | console.error('API请求错误:', error); 67 | return { 68 | code: 500, 69 | message: error instanceof Error ? error.message : '未知错误', 70 | data: null as unknown as T, 71 | }; 72 | } 73 | } -------------------------------------------------------------------------------- /web/app/services/index.ts: -------------------------------------------------------------------------------- 1 | export * from './api'; 2 | export * from './warehouseService'; 3 | export * from './githubService'; -------------------------------------------------------------------------------- /web/app/services/openaiService.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Fetches available models from OpenAI API 3 | */ 4 | export async function fetchOpenAIModels(endpoint: string, apiKey: string): Promise { 5 | try { 6 | const modelsEndpoint = `${endpoint.replace(/\/+$/, '')}/models`; 7 | 8 | const response = await fetch(modelsEndpoint, { 9 | method: 'GET', 10 | headers: { 11 | 'Content-Type': 'application/json', 12 | 'Authorization': `Bearer ${apiKey}` 13 | } 14 | }); 15 | 16 | if (!response.ok) { 17 | const error = await response.json().catch(() => ({})); 18 | throw new Error(error.error?.message || `Failed to fetch models: ${response.status}`); 19 | } 20 | 21 | const data = await response.json(); 22 | // Extract model IDs from the OpenAI API response 23 | return data.data.map((model: any) => model.id); 24 | } catch (error) { 25 | console.error('Error fetching OpenAI models:', error); 26 | throw error; 27 | } 28 | } 29 | 30 | /** 31 | * Creates a chat completion using OpenAI API 32 | */ 33 | export async function createChatCompletion( 34 | endpoint: string, 35 | apiKey: string, 36 | model: string, 37 | messages: Array<{ role: string; content: string }> 38 | ) { 39 | try { 40 | const completionsEndpoint = `${endpoint.replace(/\/+$/, '')}/chat/completions`; 41 | 42 | const response = await fetch(completionsEndpoint, { 43 | method: 'POST', 44 | headers: { 45 | 'Content-Type': 'application/json', 46 | 'Authorization': `Bearer ${apiKey}` 47 | }, 48 | body: JSON.stringify({ 49 | model, 50 | messages, 51 | stream: false 52 | }) 53 | }); 54 | 55 | if (!response.ok) { 56 | const error = await response.json().catch(() => ({})); 57 | throw new Error(error.error?.message || `Failed to create completion: ${response.status}`); 58 | } 59 | 60 | return await response.json(); 61 | } catch (error) { 62 | console.error('Error creating chat completion:', error); 63 | throw error; 64 | } 65 | } -------------------------------------------------------------------------------- /web/app/sitemap.ts: -------------------------------------------------------------------------------- 1 | import { MetadataRoute } from 'next'; 2 | import { getWarehouse } from './services/warehouseService'; 3 | 4 | // 生成动态站点地图 5 | export default async function sitemap(): Promise { 6 | try { 7 | // 获取所有仓库,每页50个 8 | const response = await getWarehouse(1, 50); 9 | const repos = response.data?.items || []; 10 | 11 | // 创建仓库主页URL,从地址中提取owner和name 12 | const repoUrls = repos.map((repo) => { 13 | // 例如:从 'https://github.com/owner/name' 提取 owner/name 14 | const addressParts = repo.address.split('/'); 15 | const repoOwner = addressParts[addressParts.length - 2] || 'unknown'; 16 | const repoName = addressParts[addressParts.length - 1] || repo.name; 17 | 18 | return { 19 | url: `https://koala.wiki/${repoOwner}/${repoName}`, 20 | lastModified: new Date(repo.updatedAt || Date.now()), 21 | changeFrequency: 'daily' as const, 22 | priority: 0.8, 23 | }; 24 | }); 25 | 26 | // 添加静态页面 27 | const staticUrls = [ 28 | { 29 | url: 'https://koala.wiki', 30 | lastModified: new Date(), 31 | changeFrequency: 'daily' as const, 32 | priority: 1.0, 33 | }, 34 | { 35 | url: 'https://koala.wiki/search', 36 | lastModified: new Date(), 37 | changeFrequency: 'weekly' as const, 38 | priority: 0.5, 39 | }, 40 | ]; 41 | 42 | return [...staticUrls, ...repoUrls]; 43 | } catch (error) { 44 | console.error('生成站点地图时出错:', error); 45 | 46 | // 发生错误时返回基本站点地图 47 | return [ 48 | { 49 | url: 'https://koala.wiki', 50 | lastModified: new Date(), 51 | changeFrequency: 'daily' as const, 52 | priority: 1.0, 53 | }, 54 | ]; 55 | } 56 | } -------------------------------------------------------------------------------- /web/app/types.ts: -------------------------------------------------------------------------------- 1 | export interface Repository { 2 | id: string | null; 3 | name: string; 4 | description: string; 5 | address: string; 6 | type: string; 7 | branch: string; 8 | /** 9 | * Repository status: 10 | * 0 = Pending (待处理) 11 | * 1 = Processing (处理中) 12 | * 2 = Completed (已完成) 13 | * 3 = Canceled (已取消) 14 | * 4 = Unauthorized (未授权) 15 | * 99 = Failed (已失败) 16 | */ 17 | status: number; 18 | prompt: string; 19 | version: string; 20 | isRecommended: boolean; 21 | createdAt: string; 22 | updatedAt?: string; 23 | error?:string; 24 | organizationName: string; 25 | success: boolean; 26 | stars: number; 27 | forks: number; 28 | avatarUrl: string; 29 | ownerUrl: string; 30 | repoUrl: string; 31 | language?: string; 32 | license?: string; 33 | } 34 | 35 | export interface RepositoryFormValues { 36 | address: string; 37 | type: string; 38 | branch: string; 39 | prompt: string; 40 | } -------------------------------------------------------------------------------- /web/app/types/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 仓库表单值接口定义 3 | */ 4 | export interface RepositoryFormValues { 5 | address?: string; // 仓库地址 (Git仓库) 6 | type?: string; // 仓库类型 7 | branch?: string; // 仓库分支 8 | enableGitAuth?: boolean; // 是否启用Git认证 9 | gitUserName?: string; // Git用户名 (私有仓库) 10 | gitPassword?: string; // Git密码/令牌 (私有仓库) 11 | submitType?: 'git' | 'upload'; // 提交方式:Git仓库或上传压缩包 12 | organization?: string; // 组织名称 (上传压缩包时) 13 | repositoryName?: string; // 仓库名称 (上传压缩包时) 14 | } 15 | 16 | /** 17 | * 仓库信息接口定义 18 | */ 19 | export interface Repository { 20 | id: string; 21 | owner?: string; // 仓库所有者/组织(兼容旧版) 22 | organizationName: string; // 仓库所有者/组织 23 | name: string; // 仓库名称 24 | address?: string; // 仓库地址 25 | type?: string; // 仓库类型,如 git 26 | branch?: string; // 分支名 27 | status: string | number; // 仓库状态 28 | description?: string; // 描述 29 | createdAt: string; // 创建时间 30 | updatedAt?: string; // 更新时间 31 | isEmbedded?: boolean; // 是否为嵌入式仓库 32 | isRecommended?: boolean; // 是否推荐 33 | error?: string; // 错误信息 34 | prompt?: string; // 提示信息 35 | version?: string; // 版本 36 | } -------------------------------------------------------------------------------- /web/next-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | 4 | // NOTE: This file should not be edited 5 | // see https://nextjs.org/docs/app/api-reference/config/typescript for more information. 6 | -------------------------------------------------------------------------------- /web/next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = { 3 | output: 'standalone', 4 | reactStrictMode: true, 5 | transpilePackages: ['antd','@ant-design/icons'], 6 | async rewrites() { 7 | // 使用占位符,在运行时会被替换 8 | const apiUrl = 'http://__API_URL_PLACEHOLDER__'; 9 | return [ 10 | { 11 | source: '/api/:path*', 12 | destination: `${apiUrl}/api/:path*`, 13 | }, 14 | ]; 15 | }, 16 | }; 17 | 18 | module.exports = nextConfig; -------------------------------------------------------------------------------- /web/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "homepage": "https://github.com/AIDotNet/OpenDeepWiki", 3 | "scripts": { 4 | "dev": "next dev", 5 | "build": "next build", 6 | "start": "next start", 7 | "lint": "next lint" 8 | }, 9 | "dependencies": { 10 | "@ant-design/cssinjs": "^1.23.0", 11 | "@ant-design/nextjs-registry": "^1.0.2", 12 | "@ant-design/v5-patch-for-react-19": "^1.0.3", 13 | "@lobehub/ui": "^2.0.12", 14 | "@types/react-syntax-highlighter": "^15.5.13", 15 | "@uiw/react-md-editor": "^4.0.6", 16 | "accept-language": "^3.0.20", 17 | "antd": "^5.24.8", 18 | "framer-motion": "^11.18.2", 19 | "i18next": "^25.2.0", 20 | "i18next-browser-languagedetector": "^8.1.0", 21 | "i18next-resources-to-backend": "^1.2.1", 22 | "lucide-react": "^0.511.0", 23 | "marked": "^15.0.11", 24 | "md-editor-rt": "^5.6.0", 25 | "mermaid": "^11.6.0", 26 | "next": "^15.3.1", 27 | "next-i18next": "^15.4.2", 28 | "react": "^19.1.0", 29 | "react-cookie": "^8.0.1", 30 | "react-dom": "^19.1.0", 31 | "react-i18next": "^15.5.1", 32 | "react-markdown": "^9.1.0", 33 | "react-syntax-highlighter": "^15.6.1", 34 | "rehype-highlight": "^7.0.2", 35 | "rehype-katex": "^7.0.1", 36 | "rehype-raw": "^7.0.0", 37 | "rehype-slug": "^6.0.0", 38 | "remark-gfm": "^4.0.1", 39 | "remark-html": "^16.0.1", 40 | "remark-math": "^6.0.0", 41 | "remark-toc": "^9.0.0" 42 | }, 43 | "devDependencies": { 44 | "@tailwindcss/postcss": "^4.1.7", 45 | "@types/node": "22.15.2", 46 | "@types/react": "19.1.2", 47 | "autoprefixer": "^10.4.21", 48 | "postcss": "^8.5.3", 49 | "tailwindcss": "^4.1.7", 50 | "typescript": "5.8.3" 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /web/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | '@tailwindcss/postcss': {}, 4 | 'autoprefixer': {}, 5 | } 6 | } -------------------------------------------------------------------------------- /web/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AIDotNet/OpenDeepWiki/ee597a3390e5b84bbd82b9066ac68aae6104cbbb/web/public/favicon.ico -------------------------------------------------------------------------------- /web/public/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AIDotNet/OpenDeepWiki/ee597a3390e5b84bbd82b9066ac68aae6104cbbb/web/public/favicon.png -------------------------------------------------------------------------------- /web/public/locales/ar/common.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "OpenDeekWiki - قاعدة معرفة الشفرة المدعومة بالذكاء الاصطناعي", 3 | "changelog": { 4 | "title": "سجل التغييرات", 5 | "github_message": "عرض تاريخ الالتزامات الكامل على GitHub", 6 | "commit": "التزام", 7 | "types": { 8 | "feature": "ميزة", 9 | "fix": "إصلاح", 10 | "docs": "وثائق", 11 | "refactor": "إعادة هيكلة", 12 | "chore": "صيانة", 13 | "style": "نمط", 14 | "perf": "أداء", 15 | "test": "اختبار", 16 | "build": "بناء", 17 | "ci": "CI/CD", 18 | "revert": "إرجاع" 19 | } 20 | }, 21 | "language": { 22 | "zh-CN": "الصينية (المبسطة)", 23 | "en-US": "الإنجليزية (الولايات المتحدة)", 24 | "zh-TW": "الصينية (التقليدية)", 25 | "ja": "اليابانية", 26 | "ko": "الكورية", 27 | "de": "الألمانية", 28 | "fr": "الفرنسية", 29 | "es": "الإسبانية", 30 | "it": "الإيطالية", 31 | "pt": "البرتغالية", 32 | "ru": "الروسية", 33 | "ar": "العربية", 34 | "hi": "الهندية", 35 | "nl": "الهولندية", 36 | "tr": "التركية", 37 | "vi": "الفيتنامية", 38 | "id": "الإندونيسية", 39 | "th": "التايلاندية", 40 | "asia": "آسيا", 41 | "europe": "أوروبا والأمريكتين", 42 | "middle_east": "الشرق الأوسط" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /web/public/locales/es/common.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "OpenDeekWiki - Base de conocimiento de código impulsada por IA", 3 | "changelog": { 4 | "title": "Registro de cambios", 5 | "github_message": "Ver historial completo de commits en GitHub", 6 | "commit": "Commit", 7 | "types": { 8 | "feature": "Característica", 9 | "fix": "Corrección", 10 | "docs": "Documentación", 11 | "refactor": "Refactorización", 12 | "chore": "Mantenimiento", 13 | "style": "Estilo", 14 | "perf": "Rendimiento", 15 | "test": "Prueba", 16 | "build": "Construcción", 17 | "ci": "CI/CD", 18 | "revert": "Reversión" 19 | } 20 | }, 21 | "language": { 22 | "zh-CN": "Chino (simplificado)", 23 | "en-US": "Inglés (EE.UU.)", 24 | "zh-TW": "Chino (tradicional)", 25 | "ja": "Japonés", 26 | "ko": "Coreano", 27 | "de": "Alemán", 28 | "fr": "Francés", 29 | "es": "Español", 30 | "it": "Italiano", 31 | "pt": "Portugués", 32 | "ru": "Ruso", 33 | "ar": "Árabe", 34 | "hi": "Hindi", 35 | "nl": "Neerlandés", 36 | "tr": "Turco", 37 | "vi": "Vietnamita", 38 | "id": "Indonesio", 39 | "th": "Tailandés", 40 | "asia": "Asia", 41 | "europe": "Europa y América", 42 | "middle_east": "Oriente Medio" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /web/public/locales/fr/common.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "OpenDeekWiki - Base de connaissances de code alimentée par l'IA", 3 | "changelog": { 4 | "title": "Journal des modifications", 5 | "github_message": "Voir l'historique complet des commits sur GitHub", 6 | "commit": "Commit", 7 | "types": { 8 | "feature": "Fonctionnalité", 9 | "fix": "Correction", 10 | "docs": "Documentation", 11 | "refactor": "Refactorisation", 12 | "chore": "Maintenance", 13 | "style": "Style", 14 | "perf": "Performance", 15 | "test": "Test", 16 | "build": "Build", 17 | "ci": "CI/CD", 18 | "revert": "Annulation" 19 | } 20 | }, 21 | "language": { 22 | "zh-CN": "Chinois (simplifié)", 23 | "en-US": "Anglais (États-Unis)", 24 | "zh-TW": "Chinois (traditionnel)", 25 | "ja": "Japonais", 26 | "ko": "Coréen", 27 | "de": "Allemand", 28 | "fr": "Français", 29 | "es": "Espagnol", 30 | "it": "Italien", 31 | "pt": "Portugais", 32 | "ru": "Russe", 33 | "ar": "Arabe", 34 | "hi": "Hindi", 35 | "nl": "Néerlandais", 36 | "tr": "Turc", 37 | "vi": "Vietnamien", 38 | "id": "Indonésien", 39 | "th": "Thaï", 40 | "asia": "Asie", 41 | "europe": "Europe & Amériques", 42 | "middle_east": "Moyen-Orient" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /web/public/locales/id/common.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "OpenDeekWiki - Basis Pengetahuan Kode Bertenaga AI", 3 | "changelog": { 4 | "title": "Log Perubahan", 5 | "github_message": "Lihat riwayat commit lengkap di GitHub", 6 | "commit": "Commit", 7 | "types": { 8 | "feature": "Fitur", 9 | "fix": "Perbaikan", 10 | "docs": "Dokumentasi", 11 | "refactor": "Refaktor", 12 | "chore": "Pemeliharaan", 13 | "style": "Gaya", 14 | "perf": "Kinerja", 15 | "test": "Tes", 16 | "build": "Build", 17 | "ci": "CI/CD", 18 | "revert": "Kembalikan" 19 | } 20 | }, 21 | "language": { 22 | "zh-CN": "Mandarin (Sederhana)", 23 | "en-US": "Inggris (AS)", 24 | "zh-TW": "Mandarin (Tradisional)", 25 | "ja": "Jepang", 26 | "ko": "Korea", 27 | "de": "Jerman", 28 | "fr": "Prancis", 29 | "es": "Spanyol", 30 | "it": "Italia", 31 | "pt": "Portugis", 32 | "ru": "Rusia", 33 | "ar": "Arab", 34 | "hi": "Hindi", 35 | "nl": "Belanda", 36 | "tr": "Turki", 37 | "vi": "Vietnam", 38 | "id": "Indonesia", 39 | "th": "Thailand", 40 | "asia": "Asia", 41 | "europe": "Eropa & Amerika", 42 | "middle_east": "Timur Tengah" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /web/public/locales/it/common.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "OpenDeekWiki - Base di conoscenza del codice basata su IA", 3 | "changelog": { 4 | "title": "Registro delle modifiche", 5 | "github_message": "Visualizza la cronologia completa dei commit su GitHub", 6 | "commit": "Commit", 7 | "types": { 8 | "feature": "Funzionalità", 9 | "fix": "Correzione", 10 | "docs": "Documentazione", 11 | "refactor": "Refactoring", 12 | "chore": "Manutenzione", 13 | "style": "Stile", 14 | "perf": "Prestazioni", 15 | "test": "Test", 16 | "build": "Build", 17 | "ci": "CI/CD", 18 | "revert": "Ripristino" 19 | } 20 | }, 21 | "language": { 22 | "zh-CN": "Cinese (semplificato)", 23 | "en-US": "Inglese (Stati Uniti)", 24 | "zh-TW": "Cinese (tradizionale)", 25 | "ja": "Giapponese", 26 | "ko": "Coreano", 27 | "de": "Tedesco", 28 | "fr": "Francese", 29 | "es": "Spagnolo", 30 | "it": "Italiano", 31 | "pt": "Portoghese", 32 | "ru": "Russo", 33 | "ar": "Arabo", 34 | "hi": "Hindi", 35 | "nl": "Olandese", 36 | "tr": "Turco", 37 | "vi": "Vietnamita", 38 | "id": "Indonesiano", 39 | "th": "Tailandese", 40 | "asia": "Asia", 41 | "europe": "Europa e Americhe", 42 | "middle_east": "Medio Oriente" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /web/public/locales/nl/common.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "OpenDeekWiki - AI-gestuurde codeconnaissancebase", 3 | "changelog": { 4 | "title": "Wijzigingslogboek", 5 | "github_message": "Bekijk volledige commit geschiedenis op GitHub", 6 | "commit": "Commit", 7 | "types": { 8 | "feature": "Functie", 9 | "fix": "Reparatie", 10 | "docs": "Documentatie", 11 | "refactor": "Refactoring", 12 | "chore": "Onderhoud", 13 | "style": "Stijl", 14 | "perf": "Prestaties", 15 | "test": "Test", 16 | "build": "Build", 17 | "ci": "CI/CD", 18 | "revert": "Terugdraaien" 19 | } 20 | }, 21 | "language": { 22 | "zh-CN": "Chinees (vereenvoudigd)", 23 | "en-US": "Engels (VS)", 24 | "zh-TW": "Chinees (traditioneel)", 25 | "ja": "Japans", 26 | "ko": "Koreaans", 27 | "de": "Duits", 28 | "fr": "Frans", 29 | "es": "Spaans", 30 | "it": "Italiaans", 31 | "pt": "Portugees", 32 | "ru": "Russisch", 33 | "ar": "Arabisch", 34 | "hi": "Hindi", 35 | "nl": "Nederlands", 36 | "tr": "Turks", 37 | "vi": "Vietnamees", 38 | "id": "Indonesisch", 39 | "th": "Thais", 40 | "asia": "Azië", 41 | "europe": "Europa & Amerika", 42 | "middle_east": "Midden-Oosten" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /web/public/locales/ru/common.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "OpenDeekWiki - База знаний кода на основе ИИ", 3 | "changelog": { 4 | "title": "Журнал изменений", 5 | "github_message": "Просмотреть полную историю коммитов на GitHub", 6 | "commit": "Коммит", 7 | "types": { 8 | "feature": "Функция", 9 | "fix": "Исправление", 10 | "docs": "Документация", 11 | "refactor": "Рефакторинг", 12 | "chore": "Обслуживание", 13 | "style": "Стиль", 14 | "perf": "Производительность", 15 | "test": "Тест", 16 | "build": "Сборка", 17 | "ci": "CI/CD", 18 | "revert": "Откат" 19 | } 20 | }, 21 | "language": { 22 | "zh-CN": "Китайский (упрощенный)", 23 | "en-US": "Английский (США)", 24 | "zh-TW": "Китайский (традиционный)", 25 | "ja": "Японский", 26 | "ko": "Корейский", 27 | "de": "Немецкий", 28 | "fr": "Французский", 29 | "es": "Испанский", 30 | "it": "Итальянский", 31 | "pt": "Португальский", 32 | "ru": "Русский", 33 | "ar": "Арабский", 34 | "hi": "Хинди", 35 | "nl": "Нидерландский", 36 | "tr": "Турецкий", 37 | "vi": "Вьетнамский", 38 | "id": "Индонезийский", 39 | "th": "Тайский", 40 | "asia": "Азия", 41 | "europe": "Европа и Америка", 42 | "middle_east": "Ближний Восток" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /web/public/locales/th/common.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "OpenDeekWiki - ฐานความรู้โค้ดที่ขับเคลื่อนด้วย AI", 3 | "changelog": { 4 | "title": "บันทึกการเปลี่ยนแปลง", 5 | "github_message": "ดูประวัติ commit ทั้งหมดบน GitHub", 6 | "commit": "Commit", 7 | "types": { 8 | "feature": "คุณสมบัติ", 9 | "fix": "แก้ไข", 10 | "docs": "เอกสาร", 11 | "refactor": "ปรับโครงสร้าง", 12 | "chore": "การบำรุงรักษา", 13 | "style": "สไตล์", 14 | "perf": "ประสิทธิภาพ", 15 | "test": "การทดสอบ", 16 | "build": "การสร้าง", 17 | "ci": "CI/CD", 18 | "revert": "ย้อนกลับ" 19 | } 20 | }, 21 | "language": { 22 | "zh-CN": "จีน (ประยุกต์)", 23 | "en-US": "อังกฤษ (สหรัฐอเมริกา)", 24 | "zh-TW": "จีน (ดั้งเดิม)", 25 | "ja": "ญี่ปุ่น", 26 | "ko": "เกาหลี", 27 | "de": "เยอรมัน", 28 | "fr": "ฝรั่งเศส", 29 | "es": "สเปน", 30 | "it": "อิตาลี", 31 | "pt": "โปรตุเกส", 32 | "ru": "รัสเซีย", 33 | "ar": "อาหรับ", 34 | "hi": "ฮินดี", 35 | "nl": "ดัตช์", 36 | "tr": "ตุรกี", 37 | "vi": "เวียดนาม", 38 | "id": "อินโดนีเซีย", 39 | "th": "ไทย", 40 | "asia": "เอเชีย", 41 | "europe": "ยุโรปและอเมริกา", 42 | "middle_east": "ตะวันออกกลาง" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /web/public/locales/tr/common.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "OpenDeekWiki - Yapay Zeka Destekli Kod Bilgi Tabanı", 3 | "changelog": { 4 | "title": "Değişiklik Günlüğü", 5 | "github_message": "GitHub'da tam commit geçmişini görüntüle", 6 | "commit": "Commit", 7 | "types": { 8 | "feature": "Özellik", 9 | "fix": "Düzeltme", 10 | "docs": "Dokümantasyon", 11 | "refactor": "Yeniden Düzenleme", 12 | "chore": "Bakım", 13 | "style": "Stil", 14 | "perf": "Performans", 15 | "test": "Test", 16 | "build": "Yapı", 17 | "ci": "CI/CD", 18 | "revert": "Geri Alma" 19 | } 20 | }, 21 | "language": { 22 | "zh-CN": "Çince (Basitleştirilmiş)", 23 | "en-US": "İngilizce (ABD)", 24 | "zh-TW": "Çince (Geleneksel)", 25 | "ja": "Japonca", 26 | "ko": "Korece", 27 | "de": "Almanca", 28 | "fr": "Fransızca", 29 | "es": "İspanyolca", 30 | "it": "İtalyanca", 31 | "pt": "Portekizce", 32 | "ru": "Rusça", 33 | "ar": "Arapça", 34 | "hi": "Hintçe", 35 | "nl": "Hollandaca", 36 | "tr": "Türkçe", 37 | "vi": "Vietnamca", 38 | "id": "Endonezce", 39 | "th": "Tayca", 40 | "asia": "Asya", 41 | "europe": "Avrupa ve Amerika", 42 | "middle_east": "Orta Doğu" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /web/public/locales/vi/common.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "OpenDeekWiki - Cơ sở kiến thức mã nguồn được hỗ trợ bởi AI", 3 | "changelog": { 4 | "title": "Nhật ký thay đổi", 5 | "github_message": "Xem lịch sử commit đầy đủ trên GitHub", 6 | "commit": "Commit", 7 | "types": { 8 | "feature": "Tính năng", 9 | "fix": "Sửa lỗi", 10 | "docs": "Tài liệu", 11 | "refactor": "Tái cấu trúc", 12 | "chore": "Bảo trì", 13 | "style": "Phong cách", 14 | "perf": "Hiệu suất", 15 | "test": "Kiểm thử", 16 | "build": "Xây dựng", 17 | "ci": "CI/CD", 18 | "revert": "Hoàn nguyên" 19 | } 20 | }, 21 | "language": { 22 | "zh-CN": "Tiếng Trung (Giản thể)", 23 | "en-US": "Tiếng Anh (Mỹ)", 24 | "zh-TW": "Tiếng Trung (Phồn thể)", 25 | "ja": "Tiếng Nhật", 26 | "ko": "Tiếng Hàn", 27 | "de": "Tiếng Đức", 28 | "fr": "Tiếng Pháp", 29 | "es": "Tiếng Tây Ban Nha", 30 | "it": "Tiếng Ý", 31 | "pt": "Tiếng Bồ Đào Nha", 32 | "ru": "Tiếng Nga", 33 | "ar": "Tiếng Ả Rập", 34 | "hi": "Tiếng Hindi", 35 | "nl": "Tiếng Hà Lan", 36 | "tr": "Tiếng Thổ Nhĩ Kỳ", 37 | "vi": "Tiếng Việt", 38 | "id": "Tiếng Indonesia", 39 | "th": "Tiếng Thái", 40 | "asia": "Châu Á", 41 | "europe": "Châu Âu & Châu Mỹ", 42 | "middle_east": "Trung Đông" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /web/public/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AIDotNet/OpenDeepWiki/ee597a3390e5b84bbd82b9066ac68aae6104cbbb/web/public/logo.png -------------------------------------------------------------------------------- /web/public/mcp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AIDotNet/OpenDeepWiki/ee597a3390e5b84bbd82b9066ac68aae6104cbbb/web/public/mcp.png -------------------------------------------------------------------------------- /web/public/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Allow: / 3 | Crawl-delay: 5 4 | sitemap:https://opendeep.wiki/api/sitemap.xml 5 | disallow:['/api/','/_next/'] -------------------------------------------------------------------------------- /web/start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -x 3 | 4 | # 设置默认API URL 5 | DEFAULT_API_URL="http://localhost:5085" 6 | API_URL="${API_URL:-$DEFAULT_API_URL}" 7 | 8 | echo "Starting application with API_URL: $API_URL" 9 | 10 | # 如果API_URL不是默认值,则进行替换 11 | if [ "$API_URL" != "http://__API_URL_PLACEHOLDER__" ]; then 12 | echo "Replacing API URL placeholder with: $API_URL" 13 | 14 | # 替换 .next 目录下所有 JavaScript 文件中的占位符 15 | find /app/.next -name "*.js" -type f -exec sed -i "s|http://__API_URL_PLACEHOLDER__|$API_URL|g" {} \; 16 | find /app/.next -name "*.json" -type f -exec sed -i "s|http://__API_URL_PLACEHOLDER__|$API_URL|g" {} \; 17 | 18 | # 替换服务端渲染文件中的占位符 19 | find /app -name "server.js" -type f -exec sed -i "s|http://__API_URL_PLACEHOLDER__|$API_URL|g" {} \; 20 | 21 | # 创建客户端运行时配置 22 | cat > /app/public/runtime-config.js << EOF 23 | window.__API_URL__ = '$API_URL'; 24 | EOF 25 | 26 | echo "API URL replacement completed" 27 | else 28 | echo "Using default API URL configuration" 29 | fi 30 | 31 | # 启动Next.js应用 32 | echo "Starting Next.js server..." 33 | exec node server.js -------------------------------------------------------------------------------- /web/styles.module.css: -------------------------------------------------------------------------------- 1 | body{ 2 | margin: 0; 3 | padding: 0; 4 | } 5 | 6 | .adminLayout { 7 | display: flex; 8 | height: 100vh; 9 | background-color: #f7f9fc; 10 | } 11 | 12 | .sidebarContainer { 13 | position: fixed; 14 | left: 0; 15 | top: 0; 16 | height: 100%; 17 | background-color: white; 18 | transition: all 0.3s; 19 | box-shadow: 0 1px 4px rgba(0, 0, 0, 0.05); 20 | z-index: 20; 21 | } 22 | 23 | .sidebarOpen { 24 | width: 16rem; /* 64px */ 25 | } 26 | 27 | .sidebarClosed { 28 | width: 5rem; /* 20px */ 29 | } 30 | 31 | .sidebarLogo { 32 | height: 4rem; 33 | display: flex; 34 | align-items: center; 35 | justify-content: center; 36 | padding: 0 1rem; 37 | border-bottom: 1px solid #f1f5f9; 38 | } 39 | 40 | .mainContent { 41 | display: flex; 42 | flex-direction: column; 43 | flex: 1; 44 | overflow: hidden; 45 | } 46 | 47 | .headerContainer { 48 | background-color: white; 49 | height: 4rem; 50 | border-bottom: 1px solid #f1f5f9; 51 | display: flex; 52 | align-items: center; 53 | padding: 0 1.5rem; 54 | position: sticky; 55 | top: 0; 56 | z-index: 10; 57 | box-shadow: 0 1px 2px rgba(0, 0, 0, 0.03); 58 | } 59 | 60 | .contentContainer { 61 | flex: 1; 62 | overflow: auto; 63 | padding: 1.5rem; 64 | } 65 | 66 | .contentWrapper { 67 | background-color: white; 68 | border-radius: 0.5rem; 69 | box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05); 70 | padding: 1.5rem; 71 | min-height: calc(100vh - 10rem); 72 | } 73 | 74 | .navItem { 75 | display: flex; 76 | align-items: center; 77 | padding: 0.75rem 1rem; 78 | border-radius: 0.5rem; 79 | transition: all 0.2s; 80 | margin-bottom: 0.5rem; 81 | color: #4b5563; 82 | } 83 | 84 | .navItemActive { 85 | background-color: #f0f7ff; 86 | color: #0771c9; 87 | font-weight: 500; 88 | } 89 | 90 | .navItemIcon { 91 | font-size: 1.25rem; 92 | } 93 | 94 | .navItemLabel { 95 | margin-left: 0.75rem; 96 | } 97 | 98 | @media (min-width: 768px) { 99 | .sidebarContainer { 100 | position: relative; 101 | } 102 | } -------------------------------------------------------------------------------- /web/tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | module.exports = { 3 | content: [ 4 | "./app/**/*.{js,ts,jsx,tsx}", 5 | "./pages/**/*.{js,ts,jsx,tsx}", 6 | "./components/**/*.{js,ts,jsx,tsx}", 7 | ], 8 | theme: { 9 | extend: { 10 | colors: { 11 | primary: { 12 | 50: '#f0f7ff', 13 | 100: '#e0effe', 14 | 200: '#bae0fd', 15 | 300: '#81c7fa', 16 | 400: '#41a7f6', 17 | 500: '#1a8ee6', 18 | 600: '#0771c9', 19 | 700: '#095ca3', 20 | 800: '#0c4d86', 21 | 900: '#0f4171', 22 | 950: '#092847', 23 | }, 24 | secondary: { 25 | 50: '#f8f8f9', 26 | 100: '#f0f1f2', 27 | 200: '#e6e8ea', 28 | 300: '#d1d6da', 29 | 400: '#b2bac1', 30 | 500: '#8e99a3', 31 | 600: '#717c87', 32 | 700: '#5c6570', 33 | 800: '#4e5660', 34 | 900: '#42484f', 35 | 950: '#25292e', 36 | }, 37 | }, 38 | }, 39 | }, 40 | plugins: [], 41 | corePlugins: { 42 | preflight: true, 43 | }, 44 | } -------------------------------------------------------------------------------- /web/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2017", 4 | "lib": [ 5 | "dom", 6 | "dom.iterable", 7 | "esnext" 8 | ], 9 | "allowJs": true, 10 | "skipLibCheck": true, 11 | "strict": false, 12 | "noEmit": true, 13 | "incremental": true, 14 | "module": "esnext", 15 | "esModuleInterop": true, 16 | "moduleResolution": "node", 17 | "resolveJsonModule": true, 18 | "isolatedModules": true, 19 | "jsx": "preserve", 20 | "plugins": [ 21 | { 22 | "name": "next" 23 | } 24 | ] 25 | }, 26 | "include": [ 27 | "next-env.d.ts", 28 | ".next/types/**/*.ts", 29 | "**/*.ts", 30 | "**/*.tsx" 31 | ], 32 | "exclude": [ 33 | "node_modules" 34 | ] 35 | } 36 | --------------------------------------------------------------------------------