- add Login
This commit is contained in:
parent
9b8ac0ef24
commit
ca62eafaad
23 changed files with 1279 additions and 284 deletions
|
|
@ -1,13 +1,13 @@
|
||||||
## WORKSPACE EXECUTIVE SUMMARY (YAML)
|
## WORKSPACE EXECUTIVE SUMMARY (YAML)
|
||||||
# Generated: 2026-05-20T06:11:22.0417511+02:00
|
# Generated: 2026-05-21T10:41:25.5071284+02:00
|
||||||
# Root: E:\ollama_net\ollama\AuthorBuddy.Web
|
# Root: E:\ollama_net\ollama\AuthorBuddy.Web
|
||||||
Architecture:
|
Architecture:
|
||||||
Type: Backend (C#/.NET)
|
Type: Backend (C#/.NET)
|
||||||
Scale: 34 relevant source files
|
Scale: 38 relevant source files
|
||||||
Components: 0
|
Components: 0
|
||||||
Controllers: 0
|
Controllers: 0
|
||||||
Models: 8
|
Models: 8
|
||||||
Services: 14
|
Services: 16
|
||||||
---
|
---
|
||||||
- File: AuthorBuddy.Web.csproj
|
- File: AuthorBuddy.Web.csproj
|
||||||
Preview: "<Project Sdk='Microsoft.NET.Sdk.Web'> <PropertyGroup> <TargetFramework>net10.0</TargetFramework> <Nullable>enable</Nullable> <ImplicitUsings>en..."
|
Preview: "<Project Sdk='Microsoft.NET.Sdk.Web'> <PropertyGroup> <TargetFramework>net10.0</TargetFramework> <Nullable>enable</Nullable> <ImplicitUsings>en..."
|
||||||
|
|
@ -18,89 +18,70 @@ Architecture:
|
||||||
- File: AuthorBuddy.Web.slnx
|
- File: AuthorBuddy.Web.slnx
|
||||||
Preview: "<Solution> <Project Path='AuthorBuddy.Web.csproj' /> </Solution> "
|
Preview: "<Solution> <Project Path='AuthorBuddy.Web.csproj' /> </Solution> "
|
||||||
- File: Program.cs
|
- File: Program.cs
|
||||||
Preview: "using AuthorBuddy.Web.Components; using AuthorBuddy.Web.Data; using AuthorBuddy.Web.Models; using AuthorBuddy.Web.Services; using LiteDB; var builder ..."
|
Preview: "using AuthorBuddy.Web.Components; using AuthorBuddy.Web.Models; using AuthorBuddy.Web.Services; using LiteDB; using Microsoft.AspNetCore.Authenticatio..."
|
||||||
- File: Data\DocumentMetaEntity.cs
|
- File: Data\DocumentMetaEntity.cs
|
||||||
Symbols: [DocumentMetaEntity]
|
|
||||||
Preview: "namespace AuthorBuddy.Web.Data; public class DocumentMetaEntity { public int Id { get; set; } public int? ProjectId { get; set; } public string ..."
|
Preview: "namespace AuthorBuddy.Web.Data; public class DocumentMetaEntity { public int Id { get; set; } public int? ProjectId { get; set; } public string ..."
|
||||||
- File: Data\DocumentVector.cs
|
- File: Data\DocumentVector.cs
|
||||||
Symbols: [DocumentVector]
|
|
||||||
Preview: "namespace AuthorBuddy.Web.Data; public class DocumentVector { public int Id { get; set; } public int DocumentMetaId { get; set; } public int Pro..."
|
Preview: "namespace AuthorBuddy.Web.Data; public class DocumentVector { public int Id { get; set; } public int DocumentMetaId { get; set; } public int Pro..."
|
||||||
- File: Data\ProjectEntity.cs
|
- File: Data\ProjectEntity.cs
|
||||||
Symbols: [ProjectEntity]
|
|
||||||
Preview: "namespace AuthorBuddy.Web.Data; public class ProjectEntity { public int Id { get; set; } public string Name { get; set; } = string.Empty; public..."
|
Preview: "namespace AuthorBuddy.Web.Data; public class ProjectEntity { public int Id { get; set; } public string Name { get; set; } = string.Empty; public..."
|
||||||
- File: Data\SettingEntity.cs
|
- File: Data\SettingEntity.cs
|
||||||
Symbols: [SettingEntity]
|
|
||||||
Preview: "using LiteDB; namespace AuthorBuddy.Web.Data; public class SettingEntity { public int Id { get; set; } public string Key { get; set; } = string.Em..."
|
Preview: "using LiteDB; namespace AuthorBuddy.Web.Data; public class SettingEntity { public int Id { get; set; } public string Key { get; set; } = string.Em..."
|
||||||
- File: Data\UserEntity.cs
|
- File: Data\UserEntity.cs
|
||||||
Symbols: [UserEntity]
|
|
||||||
Preview: "namespace AuthorBuddy.Web.Data; public class UserEntity { public int Id { get; set; } public string Name { get; set; } = string.Empty; public bo..."
|
Preview: "namespace AuthorBuddy.Web.Data; public class UserEntity { public int Id { get; set; } public string Name { get; set; } = string.Empty; public bo..."
|
||||||
- File: Models\Chapter.cs
|
- File: Models\Chapter.cs
|
||||||
Symbols: [Chapter]
|
|
||||||
Preview: "namespace AuthorBuddy.Web.Models; public class Chapter { public string Title { get; set; } = string.Empty; public string FilePath { get; set; } = ..."
|
Preview: "namespace AuthorBuddy.Web.Models; public class Chapter { public string Title { get; set; } = string.Empty; public string FilePath { get; set; } = ..."
|
||||||
- File: Models\Constants.cs
|
- File: Models\Constants.cs
|
||||||
Symbols: [Constants]
|
|
||||||
Preview: "namespace AuthorBuddy.Web.Models; public static class Constants { public static readonly string[] SubFolders = { '00_Drafts', '01_Fakten/Per..."
|
Preview: "namespace AuthorBuddy.Web.Models; public static class Constants { public static readonly string[] SubFolders = { '00_Drafts', '01_Fakten/Per..."
|
||||||
- File: Models\FileTreeItem.cs
|
- File: Models\FileTreeItem.cs
|
||||||
Symbols: [FileTreeItem]
|
|
||||||
Preview: "namespace AuthorBuddy.Web.Models; public class FileTreeItem { public string Name { get; set; } = string.Empty; public string FullPath { get; set; ..."
|
Preview: "namespace AuthorBuddy.Web.Models; public class FileTreeItem { public string Name { get; set; } = string.Empty; public string FullPath { get; set; ..."
|
||||||
- File: Models\LLM_Model.cs
|
- File: Models\LLM_Model.cs
|
||||||
Symbols: [LLM_Model, LLM_Model]
|
|
||||||
Preview: "using System.Text.Json; namespace AuthorBuddy.Web.Models { public class LLM_Model { public LLM_Model(JsonElement model) { if (mode..."
|
Preview: "using System.Text.Json; namespace AuthorBuddy.Web.Models { public class LLM_Model { public LLM_Model(JsonElement model) { if (mode..."
|
||||||
- File: Models\ModelStatus.cs
|
- File: Models\ModelStatus.cs
|
||||||
Symbols: [ModelStatus]
|
|
||||||
Preview: " //namespace AuthorBuddy.Web.Models; //public class ModelStatus //{ // public string Status { get; private set; } = 'Unknown'; // public double Prog..."
|
Preview: " //namespace AuthorBuddy.Web.Models; //public class ModelStatus //{ // public string Status { get; private set; } = 'Unknown'; // public double Prog..."
|
||||||
- File: Models\OllamaSettings.cs
|
- File: Models\OllamaSettings.cs
|
||||||
Symbols: [OllamaSettings]
|
|
||||||
Preview: "namespace AuthorBuddy.Web.Models; public class OllamaSettings { public string StyleModel { get; set; } = 'gemma4:e4b'; public string EmbedModel { ..."
|
Preview: "namespace AuthorBuddy.Web.Models; public class OllamaSettings { public string StyleModel { get; set; } = 'gemma4:e4b'; public string EmbedModel { ..."
|
||||||
- File: Models\Project.cs
|
- File: Models\Project.cs
|
||||||
Symbols: [Project]
|
|
||||||
Preview: "namespace AuthorBuddy.Web.Models; public class Project { public int Id { get; internal set; } public string Name { get; internal set; } = string.E..."
|
Preview: "namespace AuthorBuddy.Web.Models; public class Project { public int Id { get; internal set; } public string Name { get; internal set; } = string.E..."
|
||||||
- File: Models\WikipediaResult.cs
|
- File: Models\WikipediaResult.cs
|
||||||
Symbols: [WikipediaResult]
|
|
||||||
Preview: "namespace AuthorBuddy.Web.Models; public class WikipediaResult { public string Title { get; set; } = string.Empty; public string Snippet { get; se..."
|
Preview: "namespace AuthorBuddy.Web.Models; public class WikipediaResult { public string Title { get; set; } = string.Empty; public string Snippet { get; se..."
|
||||||
- File: Properties\launchSettings.json
|
- File: Properties\launchSettings.json
|
||||||
Preview: "{ '$schema': 'https://json.schemastore.org/launchsettings.json', 'profiles': { 'http': { 'commandName': 'Project', 'dotnetRunMessages': ..."
|
Preview: "{ '$schema': 'https://json.schemastore.org/launchsettings.json', 'profiles': { 'http': { 'commandName': 'Project', 'dotnetRunMessages': ..."
|
||||||
|
- File: Services\AuthService.cs
|
||||||
|
Preview: "using AuthorBuddy.Web.Data; using LiteDB; namespace AuthorBuddy.Web.Services; public class AuthService : IAuthService { private readonly ILiteDataba..."
|
||||||
|
- File: Services\CustomAuthStateProvider.cs
|
||||||
|
Preview: "using System.Security.Claims; using Microsoft.AspNetCore.Components.Authorization; namespace AuthorBuddy.Web.Services; public class CustomAuthStatePro..."
|
||||||
- File: Services\FileScannerService.cs
|
- File: Services\FileScannerService.cs
|
||||||
Symbols: [FileScannerService]
|
|
||||||
Preview: "using AuthorBuddy.Web.Data; using LiteDB; namespace AuthorBuddy.Web.Services; public class FileScannerService : IFileScannerService { private readon..."
|
Preview: "using AuthorBuddy.Web.Data; using LiteDB; namespace AuthorBuddy.Web.Services; public class FileScannerService : IFileScannerService { private readon..."
|
||||||
|
- File: Services\IAuthService.cs
|
||||||
|
Preview: "using AuthorBuddy.Web.Data; namespace AuthorBuddy.Web.Services; public interface IAuthService { Task<bool> LoginAsync(string username, string passwo..."
|
||||||
- File: Services\IFileScannerService.cs
|
- File: Services\IFileScannerService.cs
|
||||||
Symbols: [IFileScannerService]
|
|
||||||
Preview: "namespace AuthorBuddy.Web.Services; public interface IFileScannerService { Task ScanProjectFolderAsync(int projectId, string rootPath, IProgress<str..."
|
Preview: "namespace AuthorBuddy.Web.Services; public interface IFileScannerService { Task ScanProjectFolderAsync(int projectId, string rootPath, IProgress<str..."
|
||||||
- File: Services\IOllamaService.cs
|
- File: Services\IOllamaService.cs
|
||||||
Symbols: [IOllamaService]
|
|
||||||
Preview: " using AuthorBuddy.Web.Models; namespace AuthorBuddy.Web.Services; public interface IOllamaService { Task<IEnumerable<LLM_Model>> GetLocalModelsAsyn..."
|
Preview: " using AuthorBuddy.Web.Models; namespace AuthorBuddy.Web.Services; public interface IOllamaService { Task<IEnumerable<LLM_Model>> GetLocalModelsAsyn..."
|
||||||
- File: Services\IProjectService.cs
|
- File: Services\IProjectService.cs
|
||||||
Symbols: [IProjectService]
|
|
||||||
Preview: "using AuthorBuddy.Web.Models; namespace AuthorBuddy.Web.Services; public interface IProjectService { Task<int> CreateNewProjectAsync(string projectN..."
|
Preview: "using AuthorBuddy.Web.Models; namespace AuthorBuddy.Web.Services; public interface IProjectService { Task<int> CreateNewProjectAsync(string projectN..."
|
||||||
- File: Services\IRagService.cs
|
- File: Services\IRagService.cs
|
||||||
Symbols: [IRagServi, RagSearchResult]
|
|
||||||
Preview: "namespace AuthorBuddy.Web.Services; public class RagSearchResult { public string Text { get; set; } = string.Empty; public string FilePath { get; ..."
|
Preview: "namespace AuthorBuddy.Web.Services; public class RagSearchResult { public string Text { get; set; } = string.Empty; public string FilePath { get; ..."
|
||||||
- File: Services\ISettingsService.cs
|
- File: Services\ISettingsService.cs
|
||||||
Symbols: [ISettingsService]
|
|
||||||
Preview: "using AuthorBuddy.Web.Data; namespace AuthorBuddy.Web.Services; public interface ISettingsService { Task<string?> GetOllamaUrlAsync(); Task<string..."
|
Preview: "using AuthorBuddy.Web.Data; namespace AuthorBuddy.Web.Services; public interface ISettingsService { Task<string?> GetOllamaUrlAsync(); Task<string..."
|
||||||
- File: Services\IThemeService.cs
|
- File: Services\IThemeService.cs
|
||||||
Symbols: [IThemeService]
|
|
||||||
Preview: "namespace AuthorBuddy.Web.Services; public interface IThemeService { Task ApplyTheme(string theme); } "
|
Preview: "namespace AuthorBuddy.Web.Services; public interface IThemeService { Task ApplyTheme(string theme); } "
|
||||||
- File: Services\IWikipediaService.cs
|
- File: Services\IWikipediaService.cs
|
||||||
Symbols: [IWikipediaService]
|
|
||||||
Preview: "using AuthorBuddy.Web.Models; namespace AuthorBuddy.Web.Services; public interface IWikipediaService { Task<List<WikipediaResult>> SearchAsync(strin..."
|
Preview: "using AuthorBuddy.Web.Models; namespace AuthorBuddy.Web.Services; public interface IWikipediaService { Task<List<WikipediaResult>> SearchAsync(strin..."
|
||||||
- File: Services\OllamaService.cs
|
- File: Services\OllamaService.cs
|
||||||
Symbols: [OllamaService]
|
|
||||||
Preview: "using AuthorBuddy.Web.Models; using System.Text.Json; using static System.Net.Mime.MediaTypeNames; namespace AuthorBuddy.Web.Services; public class Ol..."
|
Preview: "using AuthorBuddy.Web.Models; using System.Text.Json; using static System.Net.Mime.MediaTypeNames; namespace AuthorBuddy.Web.Services; public class Ol..."
|
||||||
- File: Services\ProjectService.cs
|
- File: Services\ProjectService.cs
|
||||||
Symbols: [ProjectService]
|
|
||||||
Preview: "using AuthorBuddy.Web.Data; using AuthorBuddy.Web.Models; using LiteDB; namespace AuthorBuddy.Web.Services; public class ProjectService : IProjectServ..."
|
Preview: "using AuthorBuddy.Web.Data; using AuthorBuddy.Web.Models; using LiteDB; namespace AuthorBuddy.Web.Services; public class ProjectService : IProjectServ..."
|
||||||
- File: Services\RagService.cs
|
- File: Services\RagService.cs
|
||||||
Symbols: [RagService]
|
|
||||||
Preview: "using AuthorBuddy.Web.Data; using LiteDB; using System.Security.Cryptography; using System.Text; namespace AuthorBuddy.Web.Services; public class RagS..."
|
Preview: "using AuthorBuddy.Web.Data; using LiteDB; using System.Security.Cryptography; using System.Text; namespace AuthorBuddy.Web.Services; public class RagS..."
|
||||||
- File: Services\SettingsService.cs
|
- File: Services\SettingsService.cs
|
||||||
Symbols: [SettingsService]
|
|
||||||
Preview: "using AuthorBuddy.Web.Data; using AuthorBuddy.Web.Models; using LiteDB; namespace AuthorBuddy.Web.Services; public class SettingsService : ISettingsSe..."
|
Preview: "using AuthorBuddy.Web.Data; using AuthorBuddy.Web.Models; using LiteDB; namespace AuthorBuddy.Web.Services; public class SettingsService : ISettingsSe..."
|
||||||
- File: Services\ThemeService.cs
|
- File: Services\ThemeService.cs
|
||||||
Symbols: [ThemeService, ThemeService]
|
|
||||||
Preview: "using Microsoft.JSInterop; namespace AuthorBuddy.Web.Services; public class ThemeService : IThemeService { private readonly IJSRuntime _js; public..."
|
Preview: "using Microsoft.JSInterop; namespace AuthorBuddy.Web.Services; public class ThemeService : IThemeService { private readonly IJSRuntime _js; public..."
|
||||||
|
- File: Services\TotpHelper.cs
|
||||||
|
Preview: "using System.Security.Cryptography; namespace AuthorBuddy.Web.Services; public static class TotpHelper { private const int TotpSize = 6; private c..."
|
||||||
- File: Services\WikipediaService.cs
|
- File: Services\WikipediaService.cs
|
||||||
Symbols: [WikipediaService]
|
|
||||||
Preview: "using AuthorBuddy.Web.Models; using System.Text.Json; using System.Web; namespace AuthorBuddy.Web.Services; public class WikipediaService : IWikipedia..."
|
Preview: "using AuthorBuddy.Web.Models; using System.Text.Json; using System.Web; namespace AuthorBuddy.Web.Services; public class WikipediaService : IWikipedia..."
|
||||||
- File: Components\Layout\ReconnectModal.razor.js
|
- File: Components\Layout\ReconnectModal.razor.js
|
||||||
Preview: "// Set up event handlers const reconnectModal = document.getElementById('components-reconnect-modal'); reconnectModal.addEventListener('components-rec..."
|
Preview: "// Set up event handlers const reconnectModal = document.getElementById('components-reconnect-modal'); reconnectModal.addEventListener('components-rec..."
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,18 @@
|
||||||
"Version": 1,
|
"Version": 1,
|
||||||
"WorkspaceRootPath": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\",
|
"WorkspaceRootPath": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\",
|
||||||
"Documents": [
|
"Documents": [
|
||||||
|
{
|
||||||
|
"AbsoluteMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|e:\\ollama_net\\ollama\\authorbuddy.web\\components\\layout\\mainlayout.razor||{40D31677-CBC0-4297-A9EF-89D907823A98}",
|
||||||
|
"RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:components\\layout\\mainlayout.razor||{40D31677-CBC0-4297-A9EF-89D907823A98}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"AbsoluteMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|e:\\ollama_net\\ollama\\authorbuddy.web\\program.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
|
||||||
|
"RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:program.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"AbsoluteMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|e:\\ollama_net\\ollama\\authorbuddy.web\\services\\totphelper.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
|
||||||
|
"RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:services\\totphelper.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"AbsoluteMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|e:\\ollama_net\\ollama\\authorbuddy.web\\components\\pages\\ollamaassistant.razor||{40D31677-CBC0-4297-A9EF-89D907823A98}",
|
"AbsoluteMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|e:\\ollama_net\\ollama\\authorbuddy.web\\components\\pages\\ollamaassistant.razor||{40D31677-CBC0-4297-A9EF-89D907823A98}",
|
||||||
"RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:components\\pages\\ollamaassistant.razor||{40D31677-CBC0-4297-A9EF-89D907823A98}"
|
"RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:components\\pages\\ollamaassistant.razor||{40D31677-CBC0-4297-A9EF-89D907823A98}"
|
||||||
|
|
@ -26,18 +38,10 @@
|
||||||
"AbsoluteMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|e:\\ollama_net\\ollama\\authorbuddy.web\\services\\settingsservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
|
"AbsoluteMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|e:\\ollama_net\\ollama\\authorbuddy.web\\services\\settingsservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
|
||||||
"RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:services\\settingsservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
|
"RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:services\\settingsservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"AbsoluteMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|e:\\ollama_net\\ollama\\authorbuddy.web\\components\\layout\\mainlayout.razor||{40D31677-CBC0-4297-A9EF-89D907823A98}",
|
|
||||||
"RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:components\\layout\\mainlayout.razor||{40D31677-CBC0-4297-A9EF-89D907823A98}"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"AbsoluteMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|e:\\ollama_net\\ollama\\authorbuddy.web\\services\\isettingsservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
|
"AbsoluteMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|e:\\ollama_net\\ollama\\authorbuddy.web\\services\\isettingsservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
|
||||||
"RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:services\\isettingsservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
|
"RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:services\\isettingsservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"AbsoluteMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|e:\\ollama_net\\ollama\\authorbuddy.web\\program.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
|
|
||||||
"RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:program.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"AbsoluteMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|e:\\ollama_net\\ollama\\authorbuddy.web\\services\\projectservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
|
"AbsoluteMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|e:\\ollama_net\\ollama\\authorbuddy.web\\services\\projectservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
|
||||||
"RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:services\\projectservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
|
"RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:services\\projectservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
|
||||||
|
|
@ -126,7 +130,7 @@
|
||||||
"DocumentGroups": [
|
"DocumentGroups": [
|
||||||
{
|
{
|
||||||
"DockedWidth": 200,
|
"DockedWidth": 200,
|
||||||
"SelectedChildIndex": 2,
|
"SelectedChildIndex": 3,
|
||||||
"Children": [
|
"Children": [
|
||||||
{
|
{
|
||||||
"$type": "Bookmark",
|
"$type": "Bookmark",
|
||||||
|
|
@ -138,20 +142,106 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"$type": "Document",
|
"$type": "Document",
|
||||||
"DocumentIndex": 0,
|
"DocumentIndex": 3,
|
||||||
"Title": "OllamaAssistant.razor",
|
"Title": "OllamaAssistant.razor",
|
||||||
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\Pages\\OllamaAssistant.razor",
|
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\Pages\\OllamaAssistant.razor",
|
||||||
"RelativeDocumentMoniker": "Components\\Pages\\OllamaAssistant.razor",
|
"RelativeDocumentMoniker": "Components\\Pages\\OllamaAssistant.razor",
|
||||||
"ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\Pages\\OllamaAssistant.razor",
|
"ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\Pages\\OllamaAssistant.razor",
|
||||||
"RelativeToolTip": "Components\\Pages\\OllamaAssistant.razor",
|
"RelativeToolTip": "Components\\Pages\\OllamaAssistant.razor",
|
||||||
"ViewState": "AgIAAAkAAAAAAAAAAAAAABAAAAARAAAAAAAAAA==",
|
"ViewState": "AgIAAAkAAAAAAAAAAAAAABEAAAARAAAAAAAAAA==",
|
||||||
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000759|",
|
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000759|",
|
||||||
"WhenOpened": "2026-05-20T05:14:39.037Z",
|
"WhenOpened": "2026-05-20T05:14:39.037Z",
|
||||||
"EditorCaption": ""
|
"EditorCaption": ""
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"$type": "Document",
|
||||||
|
"DocumentIndex": 0,
|
||||||
|
"Title": "MainLayout.razor",
|
||||||
|
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\Layout\\MainLayout.razor",
|
||||||
|
"RelativeDocumentMoniker": "Components\\Layout\\MainLayout.razor",
|
||||||
|
"ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\Layout\\MainLayout.razor*",
|
||||||
|
"RelativeToolTip": "Components\\Layout\\MainLayout.razor*",
|
||||||
|
"ViewState": "AgIAAMkAAAAAAAAAAAAQwOcAAAARAAAAAAAAAA==",
|
||||||
|
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000759|",
|
||||||
|
"WhenOpened": "2026-05-15T16:04:36.444Z",
|
||||||
|
"EditorCaption": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$type": "Document",
|
||||||
|
"DocumentIndex": 6,
|
||||||
|
"Title": "OllamaSettings.cs",
|
||||||
|
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Models\\OllamaSettings.cs",
|
||||||
|
"RelativeDocumentMoniker": "Models\\OllamaSettings.cs",
|
||||||
|
"ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Models\\OllamaSettings.cs",
|
||||||
|
"RelativeToolTip": "Models\\OllamaSettings.cs",
|
||||||
|
"ViewState": "AgIAAAYAAAAAAAAAAAAkwBQAAAASAAAAAAAAAA==",
|
||||||
|
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
|
||||||
|
"WhenOpened": "2026-05-16T13:39:46.179Z"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$type": "Document",
|
||||||
|
"DocumentIndex": 17,
|
||||||
|
"Title": "AppDbContext.cs",
|
||||||
|
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Data\\AppDbContext.cs",
|
||||||
|
"RelativeDocumentMoniker": "Data\\AppDbContext.cs",
|
||||||
|
"ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Data\\AppDbContext.cs",
|
||||||
|
"RelativeToolTip": "Data\\AppDbContext.cs",
|
||||||
|
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
|
||||||
|
"WhenOpened": "2026-05-14T10:50:13.284Z"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"$type": "Document",
|
"$type": "Document",
|
||||||
"DocumentIndex": 11,
|
"DocumentIndex": 11,
|
||||||
|
"Title": "FileScannerService.cs",
|
||||||
|
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\FileScannerService.cs",
|
||||||
|
"RelativeDocumentMoniker": "Services\\FileScannerService.cs",
|
||||||
|
"ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\FileScannerService.cs",
|
||||||
|
"RelativeToolTip": "Services\\FileScannerService.cs",
|
||||||
|
"ViewState": "AgIAABIAAAAAAAAAAAAQwCUAAAAWAAAAAAAAAA==",
|
||||||
|
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
|
||||||
|
"WhenOpened": "2026-05-16T06:02:53.46Z"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$type": "Document",
|
||||||
|
"DocumentIndex": 7,
|
||||||
|
"Title": "IOllamaService.cs",
|
||||||
|
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\IOllamaService.cs",
|
||||||
|
"RelativeDocumentMoniker": "Services\\IOllamaService.cs",
|
||||||
|
"ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\IOllamaService.cs",
|
||||||
|
"RelativeToolTip": "Services\\IOllamaService.cs",
|
||||||
|
"ViewState": "AgIAAAAAAAAAAAAAAAAqwAkAAAAPAAAAAAAAAA==",
|
||||||
|
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
|
||||||
|
"WhenOpened": "2026-05-16T07:20:19.212Z"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$type": "Document",
|
||||||
|
"DocumentIndex": 1,
|
||||||
|
"Title": "Program.cs",
|
||||||
|
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Program.cs",
|
||||||
|
"RelativeDocumentMoniker": "Program.cs",
|
||||||
|
"ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Program.cs*",
|
||||||
|
"RelativeToolTip": "Program.cs*",
|
||||||
|
"ViewState": "AgIAABgAAAAAAAAAAAAAADAAAAAAAAAAAAAAAA==",
|
||||||
|
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
|
||||||
|
"WhenOpened": "2026-05-20T04:07:21.54Z",
|
||||||
|
"EditorCaption": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$type": "Document",
|
||||||
|
"DocumentIndex": 2,
|
||||||
|
"Title": "TotpHelper.cs",
|
||||||
|
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\TotpHelper.cs",
|
||||||
|
"RelativeDocumentMoniker": "Services\\TotpHelper.cs",
|
||||||
|
"ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\TotpHelper.cs",
|
||||||
|
"RelativeToolTip": "Services\\TotpHelper.cs",
|
||||||
|
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
|
||||||
|
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
|
||||||
|
"WhenOpened": "2026-05-21T08:20:49.461Z",
|
||||||
|
"EditorCaption": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$type": "Document",
|
||||||
|
"DocumentIndex": 12,
|
||||||
"Title": "Project.cs",
|
"Title": "Project.cs",
|
||||||
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Models\\Project.cs",
|
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Models\\Project.cs",
|
||||||
"RelativeDocumentMoniker": "Models\\Project.cs",
|
"RelativeDocumentMoniker": "Models\\Project.cs",
|
||||||
|
|
@ -161,72 +251,9 @@
|
||||||
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
|
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
|
||||||
"WhenOpened": "2026-05-15T15:57:10.2Z"
|
"WhenOpened": "2026-05-15T15:57:10.2Z"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"$type": "Document",
|
|
||||||
"DocumentIndex": 6,
|
|
||||||
"Title": "MainLayout.razor",
|
|
||||||
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\Layout\\MainLayout.razor",
|
|
||||||
"RelativeDocumentMoniker": "Components\\Layout\\MainLayout.razor",
|
|
||||||
"ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\Layout\\MainLayout.razor",
|
|
||||||
"RelativeToolTip": "Components\\Layout\\MainLayout.razor",
|
|
||||||
"ViewState": "AgIAALYAAAAAAAAAAAApwNMAAABAAAAAAAAAAA==",
|
|
||||||
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000759|",
|
|
||||||
"WhenOpened": "2026-05-15T16:04:36.444Z",
|
|
||||||
"EditorCaption": ""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"$type": "Document",
|
|
||||||
"DocumentIndex": 3,
|
|
||||||
"Title": "OllamaSettings.cs",
|
|
||||||
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Models\\OllamaSettings.cs",
|
|
||||||
"RelativeDocumentMoniker": "Models\\OllamaSettings.cs",
|
|
||||||
"ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Models\\OllamaSettings.cs",
|
|
||||||
"RelativeToolTip": "Models\\OllamaSettings.cs",
|
|
||||||
"ViewState": "AgIAAAYAAAAAAAAAAAAkwBQAAAASAAAAAAAAAA==",
|
|
||||||
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
|
|
||||||
"WhenOpened": "2026-05-16T13:39:46.179Z",
|
|
||||||
"EditorCaption": ""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"$type": "Document",
|
|
||||||
"DocumentIndex": 16,
|
|
||||||
"Title": "AppDbContext.cs",
|
|
||||||
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Data\\AppDbContext.cs",
|
|
||||||
"RelativeDocumentMoniker": "Data\\AppDbContext.cs",
|
|
||||||
"ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Data\\AppDbContext.cs",
|
|
||||||
"RelativeToolTip": "Data\\AppDbContext.cs",
|
|
||||||
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
|
|
||||||
"WhenOpened": "2026-05-14T10:50:13.284Z"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"$type": "Document",
|
|
||||||
"DocumentIndex": 10,
|
|
||||||
"Title": "FileScannerService.cs",
|
|
||||||
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\FileScannerService.cs",
|
|
||||||
"RelativeDocumentMoniker": "Services\\FileScannerService.cs",
|
|
||||||
"ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\FileScannerService.cs",
|
|
||||||
"RelativeToolTip": "Services\\FileScannerService.cs",
|
|
||||||
"ViewState": "AgIAABIAAAAAAAAAAAAQwCUAAAAWAAAAAAAAAA==",
|
|
||||||
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
|
|
||||||
"WhenOpened": "2026-05-16T06:02:53.46Z",
|
|
||||||
"EditorCaption": ""
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"$type": "Document",
|
"$type": "Document",
|
||||||
"DocumentIndex": 4,
|
"DocumentIndex": 4,
|
||||||
"Title": "IOllamaService.cs",
|
|
||||||
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\IOllamaService.cs",
|
|
||||||
"RelativeDocumentMoniker": "Services\\IOllamaService.cs",
|
|
||||||
"ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\IOllamaService.cs",
|
|
||||||
"RelativeToolTip": "Services\\IOllamaService.cs",
|
|
||||||
"ViewState": "AgIAAAAAAAAAAAAAAAAqwAkAAAAPAAAAAAAAAA==",
|
|
||||||
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
|
|
||||||
"WhenOpened": "2026-05-16T07:20:19.212Z",
|
|
||||||
"EditorCaption": ""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"$type": "Document",
|
|
||||||
"DocumentIndex": 1,
|
|
||||||
"Title": "Settings.razor",
|
"Title": "Settings.razor",
|
||||||
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\Pages\\Settings.razor",
|
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\Pages\\Settings.razor",
|
||||||
"RelativeDocumentMoniker": "Components\\Pages\\Settings.razor",
|
"RelativeDocumentMoniker": "Components\\Pages\\Settings.razor",
|
||||||
|
|
@ -234,12 +261,11 @@
|
||||||
"RelativeToolTip": "Components\\Pages\\Settings.razor",
|
"RelativeToolTip": "Components\\Pages\\Settings.razor",
|
||||||
"ViewState": "AgIAAAwAAAAAAAAAAAAAAIYAAAAlAAAAAAAAAA==",
|
"ViewState": "AgIAAAwAAAAAAAAAAAAAAIYAAAAlAAAAAAAAAA==",
|
||||||
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000759|",
|
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000759|",
|
||||||
"WhenOpened": "2026-05-15T16:43:26.6Z",
|
"WhenOpened": "2026-05-15T16:43:26.6Z"
|
||||||
"EditorCaption": ""
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"$type": "Document",
|
"$type": "Document",
|
||||||
"DocumentIndex": 5,
|
"DocumentIndex": 8,
|
||||||
"Title": "SettingsService.cs",
|
"Title": "SettingsService.cs",
|
||||||
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\SettingsService.cs",
|
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\SettingsService.cs",
|
||||||
"RelativeDocumentMoniker": "Services\\SettingsService.cs",
|
"RelativeDocumentMoniker": "Services\\SettingsService.cs",
|
||||||
|
|
@ -247,12 +273,11 @@
|
||||||
"RelativeToolTip": "Services\\SettingsService.cs",
|
"RelativeToolTip": "Services\\SettingsService.cs",
|
||||||
"ViewState": "AgIAAEUBAAAAAAAAAAAQwFwBAAAAAAAAAAAAAA==",
|
"ViewState": "AgIAAEUBAAAAAAAAAAAQwFwBAAAAAAAAAAAAAA==",
|
||||||
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
|
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
|
||||||
"WhenOpened": "2026-05-16T06:05:12.928Z",
|
"WhenOpened": "2026-05-16T06:05:12.928Z"
|
||||||
"EditorCaption": ""
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"$type": "Document",
|
"$type": "Document",
|
||||||
"DocumentIndex": 2,
|
"DocumentIndex": 5,
|
||||||
"Title": "OllamaService.cs",
|
"Title": "OllamaService.cs",
|
||||||
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\OllamaService.cs",
|
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\OllamaService.cs",
|
||||||
"RelativeDocumentMoniker": "Services\\OllamaService.cs",
|
"RelativeDocumentMoniker": "Services\\OllamaService.cs",
|
||||||
|
|
@ -260,12 +285,11 @@
|
||||||
"RelativeToolTip": "Services\\OllamaService.cs",
|
"RelativeToolTip": "Services\\OllamaService.cs",
|
||||||
"ViewState": "AgIAAFkAAAAAAAAAAAAqwGsAAAAeAAAAAAAAAA==",
|
"ViewState": "AgIAAFkAAAAAAAAAAAAqwGsAAAAeAAAAAAAAAA==",
|
||||||
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
|
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
|
||||||
"WhenOpened": "2026-05-16T06:43:46.404Z",
|
"WhenOpened": "2026-05-16T06:43:46.404Z"
|
||||||
"EditorCaption": ""
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"$type": "Document",
|
"$type": "Document",
|
||||||
"DocumentIndex": 12,
|
"DocumentIndex": 13,
|
||||||
"Title": "RagService.cs",
|
"Title": "RagService.cs",
|
||||||
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\RagService.cs",
|
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\RagService.cs",
|
||||||
"RelativeDocumentMoniker": "Services\\RagService.cs",
|
"RelativeDocumentMoniker": "Services\\RagService.cs",
|
||||||
|
|
@ -277,7 +301,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"$type": "Document",
|
"$type": "Document",
|
||||||
"DocumentIndex": 7,
|
"DocumentIndex": 9,
|
||||||
"Title": "ISettingsService.cs",
|
"Title": "ISettingsService.cs",
|
||||||
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\ISettingsService.cs",
|
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\ISettingsService.cs",
|
||||||
"RelativeDocumentMoniker": "Services\\ISettingsService.cs",
|
"RelativeDocumentMoniker": "Services\\ISettingsService.cs",
|
||||||
|
|
@ -285,25 +309,11 @@
|
||||||
"RelativeToolTip": "Services\\ISettingsService.cs",
|
"RelativeToolTip": "Services\\ISettingsService.cs",
|
||||||
"ViewState": "AgIAABoAAAAAAAAAAAA8wCoAAAAPAAAAAAAAAA==",
|
"ViewState": "AgIAABoAAAAAAAAAAAA8wCoAAAAPAAAAAAAAAA==",
|
||||||
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
|
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
|
||||||
"WhenOpened": "2026-05-16T13:21:26.951Z",
|
"WhenOpened": "2026-05-16T13:21:26.951Z"
|
||||||
"EditorCaption": ""
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"$type": "Document",
|
"$type": "Document",
|
||||||
"DocumentIndex": 8,
|
"DocumentIndex": 14,
|
||||||
"Title": "Program.cs",
|
|
||||||
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Program.cs",
|
|
||||||
"RelativeDocumentMoniker": "Program.cs",
|
|
||||||
"ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Program.cs",
|
|
||||||
"RelativeToolTip": "Program.cs",
|
|
||||||
"ViewState": "AgIAAAMAAAAAAAAAAAApwBoAAAANAAAAAAAAAA==",
|
|
||||||
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
|
|
||||||
"WhenOpened": "2026-05-20T04:07:21.54Z",
|
|
||||||
"EditorCaption": ""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"$type": "Document",
|
|
||||||
"DocumentIndex": 13,
|
|
||||||
"Title": "IRagService.cs",
|
"Title": "IRagService.cs",
|
||||||
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\IRagService.cs",
|
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\IRagService.cs",
|
||||||
"RelativeDocumentMoniker": "Services\\IRagService.cs",
|
"RelativeDocumentMoniker": "Services\\IRagService.cs",
|
||||||
|
|
@ -315,7 +325,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"$type": "Document",
|
"$type": "Document",
|
||||||
"DocumentIndex": 9,
|
"DocumentIndex": 10,
|
||||||
"Title": "ProjectService.cs",
|
"Title": "ProjectService.cs",
|
||||||
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\ProjectService.cs",
|
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\ProjectService.cs",
|
||||||
"RelativeDocumentMoniker": "Services\\ProjectService.cs",
|
"RelativeDocumentMoniker": "Services\\ProjectService.cs",
|
||||||
|
|
@ -323,12 +333,11 @@
|
||||||
"RelativeToolTip": "Services\\ProjectService.cs",
|
"RelativeToolTip": "Services\\ProjectService.cs",
|
||||||
"ViewState": "AgIAADQAAAAAAAAAAAAqwEYAAAAWAAAAAAAAAA==",
|
"ViewState": "AgIAADQAAAAAAAAAAAAqwEYAAAAWAAAAAAAAAA==",
|
||||||
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
|
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
|
||||||
"WhenOpened": "2026-05-15T15:56:55.229Z",
|
"WhenOpened": "2026-05-15T15:56:55.229Z"
|
||||||
"EditorCaption": ""
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"$type": "Document",
|
"$type": "Document",
|
||||||
"DocumentIndex": 15,
|
"DocumentIndex": 16,
|
||||||
"Title": "NavMenu.razor",
|
"Title": "NavMenu.razor",
|
||||||
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\Layout\\NavMenu.razor",
|
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\Layout\\NavMenu.razor",
|
||||||
"RelativeDocumentMoniker": "Components\\Layout\\NavMenu.razor",
|
"RelativeDocumentMoniker": "Components\\Layout\\NavMenu.razor",
|
||||||
|
|
@ -340,7 +349,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"$type": "Document",
|
"$type": "Document",
|
||||||
"DocumentIndex": 14,
|
"DocumentIndex": 15,
|
||||||
"Title": "Editor.razor",
|
"Title": "Editor.razor",
|
||||||
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\Pages\\Editor.razor",
|
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\Pages\\Editor.razor",
|
||||||
"RelativeDocumentMoniker": "Components\\Pages\\Editor.razor",
|
"RelativeDocumentMoniker": "Components\\Pages\\Editor.razor",
|
||||||
|
|
@ -353,7 +362,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"$type": "Document",
|
"$type": "Document",
|
||||||
"DocumentIndex": 17,
|
"DocumentIndex": 18,
|
||||||
"Title": "ThemeService.cs",
|
"Title": "ThemeService.cs",
|
||||||
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\ThemeService.cs",
|
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\ThemeService.cs",
|
||||||
"RelativeDocumentMoniker": "Services\\ThemeService.cs",
|
"RelativeDocumentMoniker": "Services\\ThemeService.cs",
|
||||||
|
|
@ -365,7 +374,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"$type": "Document",
|
"$type": "Document",
|
||||||
"DocumentIndex": 18,
|
"DocumentIndex": 19,
|
||||||
"Title": "App.razor",
|
"Title": "App.razor",
|
||||||
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\App.razor",
|
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\App.razor",
|
||||||
"RelativeDocumentMoniker": "Components\\App.razor",
|
"RelativeDocumentMoniker": "Components\\App.razor",
|
||||||
|
|
@ -377,7 +386,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"$type": "Document",
|
"$type": "Document",
|
||||||
"DocumentIndex": 21,
|
"DocumentIndex": 22,
|
||||||
"Title": "Models.razor",
|
"Title": "Models.razor",
|
||||||
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\Pages\\Models.razor",
|
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\Pages\\Models.razor",
|
||||||
"RelativeDocumentMoniker": "Components\\Pages\\Models.razor",
|
"RelativeDocumentMoniker": "Components\\Pages\\Models.razor",
|
||||||
|
|
@ -389,7 +398,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"$type": "Document",
|
"$type": "Document",
|
||||||
"DocumentIndex": 19,
|
"DocumentIndex": 20,
|
||||||
"Title": "LLM_Model.cs",
|
"Title": "LLM_Model.cs",
|
||||||
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Models\\LLM_Model.cs",
|
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Models\\LLM_Model.cs",
|
||||||
"RelativeDocumentMoniker": "Models\\LLM_Model.cs",
|
"RelativeDocumentMoniker": "Models\\LLM_Model.cs",
|
||||||
|
|
@ -401,7 +410,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"$type": "Document",
|
"$type": "Document",
|
||||||
"DocumentIndex": 20,
|
"DocumentIndex": 21,
|
||||||
"Title": "ModelStatus.cs",
|
"Title": "ModelStatus.cs",
|
||||||
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Models\\ModelStatus.cs",
|
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Models\\ModelStatus.cs",
|
||||||
"RelativeDocumentMoniker": "Models\\ModelStatus.cs",
|
"RelativeDocumentMoniker": "Models\\ModelStatus.cs",
|
||||||
|
|
@ -413,7 +422,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"$type": "Document",
|
"$type": "Document",
|
||||||
"DocumentIndex": 24,
|
"DocumentIndex": 25,
|
||||||
"Title": "MainLayout.razor.css",
|
"Title": "MainLayout.razor.css",
|
||||||
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\Layout\\MainLayout.razor.css",
|
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\Layout\\MainLayout.razor.css",
|
||||||
"RelativeDocumentMoniker": "Components\\Layout\\MainLayout.razor.css",
|
"RelativeDocumentMoniker": "Components\\Layout\\MainLayout.razor.css",
|
||||||
|
|
@ -425,7 +434,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"$type": "Document",
|
"$type": "Document",
|
||||||
"DocumentIndex": 22,
|
"DocumentIndex": 23,
|
||||||
"Title": "IFileScannerService.cs",
|
"Title": "IFileScannerService.cs",
|
||||||
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\IFileScannerService.cs",
|
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\IFileScannerService.cs",
|
||||||
"RelativeDocumentMoniker": "Services\\IFileScannerService.cs",
|
"RelativeDocumentMoniker": "Services\\IFileScannerService.cs",
|
||||||
|
|
@ -437,7 +446,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"$type": "Document",
|
"$type": "Document",
|
||||||
"DocumentIndex": 23,
|
"DocumentIndex": 24,
|
||||||
"Title": "bootstrap-utilities.css",
|
"Title": "bootstrap-utilities.css",
|
||||||
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\wwwroot\\lib\\bootstrap\\dist\\css\\bootstrap-utilities.css",
|
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\wwwroot\\lib\\bootstrap\\dist\\css\\bootstrap-utilities.css",
|
||||||
"RelativeDocumentMoniker": "wwwroot\\lib\\bootstrap\\dist\\css\\bootstrap-utilities.css",
|
"RelativeDocumentMoniker": "wwwroot\\lib\\bootstrap\\dist\\css\\bootstrap-utilities.css",
|
||||||
|
|
@ -449,7 +458,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"$type": "Document",
|
"$type": "Document",
|
||||||
"DocumentIndex": 26,
|
"DocumentIndex": 27,
|
||||||
"Title": "ProjectEntity.cs",
|
"Title": "ProjectEntity.cs",
|
||||||
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Data\\ProjectEntity.cs",
|
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Data\\ProjectEntity.cs",
|
||||||
"RelativeDocumentMoniker": "Data\\ProjectEntity.cs",
|
"RelativeDocumentMoniker": "Data\\ProjectEntity.cs",
|
||||||
|
|
@ -461,7 +470,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"$type": "Document",
|
"$type": "Document",
|
||||||
"DocumentIndex": 25,
|
"DocumentIndex": 26,
|
||||||
"Title": "IProjectService.cs",
|
"Title": "IProjectService.cs",
|
||||||
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\IProjectService.cs",
|
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\IProjectService.cs",
|
||||||
"RelativeDocumentMoniker": "Services\\IProjectService.cs",
|
"RelativeDocumentMoniker": "Services\\IProjectService.cs",
|
||||||
|
|
@ -473,7 +482,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"$type": "Document",
|
"$type": "Document",
|
||||||
"DocumentIndex": 27,
|
"DocumentIndex": 28,
|
||||||
"Title": "AuthorBuddy.Web.csproj",
|
"Title": "AuthorBuddy.Web.csproj",
|
||||||
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\AuthorBuddy.Web.csproj",
|
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\AuthorBuddy.Web.csproj",
|
||||||
"RelativeDocumentMoniker": "AuthorBuddy.Web.csproj",
|
"RelativeDocumentMoniker": "AuthorBuddy.Web.csproj",
|
||||||
|
|
@ -485,7 +494,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"$type": "Document",
|
"$type": "Document",
|
||||||
"DocumentIndex": 28,
|
"DocumentIndex": 29,
|
||||||
"Title": "appsettings.json",
|
"Title": "appsettings.json",
|
||||||
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\appsettings.json",
|
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\appsettings.json",
|
||||||
"RelativeDocumentMoniker": "appsettings.json",
|
"RelativeDocumentMoniker": "appsettings.json",
|
||||||
|
|
|
||||||
|
|
@ -2,14 +2,26 @@
|
||||||
"Version": 1,
|
"Version": 1,
|
||||||
"WorkspaceRootPath": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\",
|
"WorkspaceRootPath": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\",
|
||||||
"Documents": [
|
"Documents": [
|
||||||
{
|
|
||||||
"AbsoluteMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|e:\\ollama_net\\ollama\\authorbuddy.web\\components\\pages\\ollamaassistant.razor||{40D31677-CBC0-4297-A9EF-89D907823A98}",
|
|
||||||
"RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:components\\pages\\ollamaassistant.razor||{40D31677-CBC0-4297-A9EF-89D907823A98}"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"AbsoluteMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|e:\\ollama_net\\ollama\\authorbuddy.web\\components\\layout\\mainlayout.razor||{40D31677-CBC0-4297-A9EF-89D907823A98}",
|
"AbsoluteMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|e:\\ollama_net\\ollama\\authorbuddy.web\\components\\layout\\mainlayout.razor||{40D31677-CBC0-4297-A9EF-89D907823A98}",
|
||||||
"RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:components\\layout\\mainlayout.razor||{40D31677-CBC0-4297-A9EF-89D907823A98}"
|
"RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:components\\layout\\mainlayout.razor||{40D31677-CBC0-4297-A9EF-89D907823A98}"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"AbsoluteMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|e:\\ollama_net\\ollama\\authorbuddy.web\\program.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
|
||||||
|
"RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:program.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"AbsoluteMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|e:\\ollama_net\\ollama\\authorbuddy.web\\components\\pages\\login.razor||{40D31677-CBC0-4297-A9EF-89D907823A98}",
|
||||||
|
"RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:components\\pages\\login.razor||{40D31677-CBC0-4297-A9EF-89D907823A98}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"AbsoluteMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|e:\\ollama_net\\ollama\\authorbuddy.web\\services\\totphelper.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
|
||||||
|
"RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:services\\totphelper.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"AbsoluteMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|e:\\ollama_net\\ollama\\authorbuddy.web\\components\\pages\\ollamaassistant.razor||{40D31677-CBC0-4297-A9EF-89D907823A98}",
|
||||||
|
"RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:components\\pages\\ollamaassistant.razor||{40D31677-CBC0-4297-A9EF-89D907823A98}"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"AbsoluteMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|e:\\ollama_net\\ollama\\authorbuddy.web\\components\\pages\\settings.razor||{40D31677-CBC0-4297-A9EF-89D907823A98}",
|
"AbsoluteMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|e:\\ollama_net\\ollama\\authorbuddy.web\\components\\pages\\settings.razor||{40D31677-CBC0-4297-A9EF-89D907823A98}",
|
||||||
"RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:components\\pages\\settings.razor||{40D31677-CBC0-4297-A9EF-89D907823A98}"
|
"RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:components\\pages\\settings.razor||{40D31677-CBC0-4297-A9EF-89D907823A98}"
|
||||||
|
|
@ -34,10 +46,6 @@
|
||||||
"AbsoluteMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|e:\\ollama_net\\ollama\\authorbuddy.web\\services\\isettingsservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
|
"AbsoluteMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|e:\\ollama_net\\ollama\\authorbuddy.web\\services\\isettingsservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
|
||||||
"RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:services\\isettingsservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
|
"RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:services\\isettingsservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"AbsoluteMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|e:\\ollama_net\\ollama\\authorbuddy.web\\program.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
|
|
||||||
"RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:program.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"AbsoluteMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|e:\\ollama_net\\ollama\\authorbuddy.web\\services\\projectservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
|
"AbsoluteMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|e:\\ollama_net\\ollama\\authorbuddy.web\\services\\projectservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
|
||||||
"RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:services\\projectservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
|
"RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:services\\projectservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
|
||||||
|
|
@ -126,7 +134,7 @@
|
||||||
"DocumentGroups": [
|
"DocumentGroups": [
|
||||||
{
|
{
|
||||||
"DockedWidth": 200,
|
"DockedWidth": 200,
|
||||||
"SelectedChildIndex": 2,
|
"SelectedChildIndex": 3,
|
||||||
"Children": [
|
"Children": [
|
||||||
{
|
{
|
||||||
"$type": "Bookmark",
|
"$type": "Bookmark",
|
||||||
|
|
@ -136,22 +144,121 @@
|
||||||
"$type": "Bookmark",
|
"$type": "Bookmark",
|
||||||
"Name": "RST:0:0:{b3f9f954-0ca5-0091-76b4-34a47f44c954}"
|
"Name": "RST:0:0:{b3f9f954-0ca5-0091-76b4-34a47f44c954}"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"$type": "Document",
|
||||||
|
"DocumentIndex": 2,
|
||||||
|
"Title": "Login.razor",
|
||||||
|
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\Pages\\Login.razor",
|
||||||
|
"RelativeDocumentMoniker": "Components\\Pages\\Login.razor",
|
||||||
|
"ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\Pages\\Login.razor",
|
||||||
|
"RelativeToolTip": "Components\\Pages\\Login.razor",
|
||||||
|
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAA==",
|
||||||
|
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000759|",
|
||||||
|
"WhenOpened": "2026-05-21T08:47:41.996Z",
|
||||||
|
"EditorCaption": ""
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"$type": "Document",
|
"$type": "Document",
|
||||||
"DocumentIndex": 0,
|
"DocumentIndex": 0,
|
||||||
|
"Title": "MainLayout.razor",
|
||||||
|
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\Layout\\MainLayout.razor",
|
||||||
|
"RelativeDocumentMoniker": "Components\\Layout\\MainLayout.razor",
|
||||||
|
"ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\Layout\\MainLayout.razor",
|
||||||
|
"RelativeToolTip": "Components\\Layout\\MainLayout.razor",
|
||||||
|
"ViewState": "AgIAAI0AAAAAAAAAAAAAAHIBAAAgAAAAAAAAAA==",
|
||||||
|
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000759|",
|
||||||
|
"WhenOpened": "2026-05-15T16:04:36.444Z",
|
||||||
|
"EditorCaption": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$type": "Document",
|
||||||
|
"DocumentIndex": 1,
|
||||||
|
"Title": "Program.cs",
|
||||||
|
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Program.cs",
|
||||||
|
"RelativeDocumentMoniker": "Program.cs",
|
||||||
|
"ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Program.cs",
|
||||||
|
"RelativeToolTip": "Program.cs",
|
||||||
|
"ViewState": "AgIAAAsAAAAAAAAAAAApwB8AAAAeAAAAAAAAAA==",
|
||||||
|
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
|
||||||
|
"WhenOpened": "2026-05-20T04:07:21.54Z",
|
||||||
|
"EditorCaption": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$type": "Document",
|
||||||
|
"DocumentIndex": 7,
|
||||||
|
"Title": "OllamaSettings.cs",
|
||||||
|
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Models\\OllamaSettings.cs",
|
||||||
|
"RelativeDocumentMoniker": "Models\\OllamaSettings.cs",
|
||||||
|
"ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Models\\OllamaSettings.cs",
|
||||||
|
"RelativeToolTip": "Models\\OllamaSettings.cs",
|
||||||
|
"ViewState": "AgIAAAYAAAAAAAAAAAAkwBQAAAASAAAAAAAAAA==",
|
||||||
|
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
|
||||||
|
"WhenOpened": "2026-05-16T13:39:46.179Z"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$type": "Document",
|
||||||
|
"DocumentIndex": 18,
|
||||||
|
"Title": "AppDbContext.cs",
|
||||||
|
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Data\\AppDbContext.cs",
|
||||||
|
"RelativeDocumentMoniker": "Data\\AppDbContext.cs",
|
||||||
|
"ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Data\\AppDbContext.cs",
|
||||||
|
"RelativeToolTip": "Data\\AppDbContext.cs",
|
||||||
|
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
|
||||||
|
"WhenOpened": "2026-05-14T10:50:13.284Z"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$type": "Document",
|
||||||
|
"DocumentIndex": 12,
|
||||||
|
"Title": "FileScannerService.cs",
|
||||||
|
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\FileScannerService.cs",
|
||||||
|
"RelativeDocumentMoniker": "Services\\FileScannerService.cs",
|
||||||
|
"ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\FileScannerService.cs",
|
||||||
|
"RelativeToolTip": "Services\\FileScannerService.cs",
|
||||||
|
"ViewState": "AgIAABIAAAAAAAAAAAAQwCUAAAAWAAAAAAAAAA==",
|
||||||
|
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
|
||||||
|
"WhenOpened": "2026-05-16T06:02:53.46Z"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$type": "Document",
|
||||||
|
"DocumentIndex": 8,
|
||||||
|
"Title": "IOllamaService.cs",
|
||||||
|
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\IOllamaService.cs",
|
||||||
|
"RelativeDocumentMoniker": "Services\\IOllamaService.cs",
|
||||||
|
"ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\IOllamaService.cs",
|
||||||
|
"RelativeToolTip": "Services\\IOllamaService.cs",
|
||||||
|
"ViewState": "AgIAAAAAAAAAAAAAAAAqwAkAAAAPAAAAAAAAAA==",
|
||||||
|
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
|
||||||
|
"WhenOpened": "2026-05-16T07:20:19.212Z"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$type": "Document",
|
||||||
|
"DocumentIndex": 4,
|
||||||
"Title": "OllamaAssistant.razor",
|
"Title": "OllamaAssistant.razor",
|
||||||
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\Pages\\OllamaAssistant.razor",
|
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\Pages\\OllamaAssistant.razor",
|
||||||
"RelativeDocumentMoniker": "Components\\Pages\\OllamaAssistant.razor",
|
"RelativeDocumentMoniker": "Components\\Pages\\OllamaAssistant.razor",
|
||||||
"ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\Pages\\OllamaAssistant.razor*",
|
"ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\Pages\\OllamaAssistant.razor",
|
||||||
"RelativeToolTip": "Components\\Pages\\OllamaAssistant.razor*",
|
"RelativeToolTip": "Components\\Pages\\OllamaAssistant.razor",
|
||||||
"ViewState": "AgIAAAkAAAAAAAAAAAAAABEAAABsAAAAAAAAAA==",
|
"ViewState": "AgIAAAkAAAAAAAAAAAAAABEAAAARAAAAAAAAAA==",
|
||||||
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000759|",
|
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000759|",
|
||||||
"WhenOpened": "2026-05-20T05:14:39.037Z",
|
"WhenOpened": "2026-05-20T05:14:39.037Z",
|
||||||
"EditorCaption": ""
|
"EditorCaption": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"$type": "Document",
|
"$type": "Document",
|
||||||
"DocumentIndex": 11,
|
"DocumentIndex": 3,
|
||||||
|
"Title": "TotpHelper.cs",
|
||||||
|
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\TotpHelper.cs",
|
||||||
|
"RelativeDocumentMoniker": "Services\\TotpHelper.cs",
|
||||||
|
"ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\TotpHelper.cs",
|
||||||
|
"RelativeToolTip": "Services\\TotpHelper.cs",
|
||||||
|
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
|
||||||
|
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
|
||||||
|
"WhenOpened": "2026-05-21T08:20:49.461Z",
|
||||||
|
"EditorCaption": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$type": "Document",
|
||||||
|
"DocumentIndex": 13,
|
||||||
"Title": "Project.cs",
|
"Title": "Project.cs",
|
||||||
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Models\\Project.cs",
|
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Models\\Project.cs",
|
||||||
"RelativeDocumentMoniker": "Models\\Project.cs",
|
"RelativeDocumentMoniker": "Models\\Project.cs",
|
||||||
|
|
@ -161,72 +268,9 @@
|
||||||
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
|
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
|
||||||
"WhenOpened": "2026-05-15T15:57:10.2Z"
|
"WhenOpened": "2026-05-15T15:57:10.2Z"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"$type": "Document",
|
|
||||||
"DocumentIndex": 1,
|
|
||||||
"Title": "MainLayout.razor",
|
|
||||||
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\Layout\\MainLayout.razor",
|
|
||||||
"RelativeDocumentMoniker": "Components\\Layout\\MainLayout.razor",
|
|
||||||
"ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\Layout\\MainLayout.razor",
|
|
||||||
"RelativeToolTip": "Components\\Layout\\MainLayout.razor",
|
|
||||||
"ViewState": "AgIAAH8AAAAAAAAAAAApwJMAAAAyAAAAAAAAAA==",
|
|
||||||
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000759|",
|
|
||||||
"WhenOpened": "2026-05-15T16:04:36.444Z",
|
|
||||||
"EditorCaption": ""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"$type": "Document",
|
|
||||||
"DocumentIndex": 4,
|
|
||||||
"Title": "OllamaSettings.cs",
|
|
||||||
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Models\\OllamaSettings.cs",
|
|
||||||
"RelativeDocumentMoniker": "Models\\OllamaSettings.cs",
|
|
||||||
"ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Models\\OllamaSettings.cs",
|
|
||||||
"RelativeToolTip": "Models\\OllamaSettings.cs",
|
|
||||||
"ViewState": "AgIAAAYAAAAAAAAAAAAkwBQAAAASAAAAAAAAAA==",
|
|
||||||
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
|
|
||||||
"WhenOpened": "2026-05-16T13:39:46.179Z",
|
|
||||||
"EditorCaption": ""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"$type": "Document",
|
|
||||||
"DocumentIndex": 16,
|
|
||||||
"Title": "AppDbContext.cs",
|
|
||||||
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Data\\AppDbContext.cs",
|
|
||||||
"RelativeDocumentMoniker": "Data\\AppDbContext.cs",
|
|
||||||
"ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Data\\AppDbContext.cs",
|
|
||||||
"RelativeToolTip": "Data\\AppDbContext.cs",
|
|
||||||
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
|
|
||||||
"WhenOpened": "2026-05-14T10:50:13.284Z"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"$type": "Document",
|
|
||||||
"DocumentIndex": 10,
|
|
||||||
"Title": "FileScannerService.cs",
|
|
||||||
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\FileScannerService.cs",
|
|
||||||
"RelativeDocumentMoniker": "Services\\FileScannerService.cs",
|
|
||||||
"ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\FileScannerService.cs",
|
|
||||||
"RelativeToolTip": "Services\\FileScannerService.cs",
|
|
||||||
"ViewState": "AgIAABIAAAAAAAAAAAAQwCUAAAAWAAAAAAAAAA==",
|
|
||||||
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
|
|
||||||
"WhenOpened": "2026-05-16T06:02:53.46Z",
|
|
||||||
"EditorCaption": ""
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"$type": "Document",
|
"$type": "Document",
|
||||||
"DocumentIndex": 5,
|
"DocumentIndex": 5,
|
||||||
"Title": "IOllamaService.cs",
|
|
||||||
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\IOllamaService.cs",
|
|
||||||
"RelativeDocumentMoniker": "Services\\IOllamaService.cs",
|
|
||||||
"ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\IOllamaService.cs",
|
|
||||||
"RelativeToolTip": "Services\\IOllamaService.cs",
|
|
||||||
"ViewState": "AgIAAAAAAAAAAAAAAAAqwAkAAAAPAAAAAAAAAA==",
|
|
||||||
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
|
|
||||||
"WhenOpened": "2026-05-16T07:20:19.212Z",
|
|
||||||
"EditorCaption": ""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"$type": "Document",
|
|
||||||
"DocumentIndex": 2,
|
|
||||||
"Title": "Settings.razor",
|
"Title": "Settings.razor",
|
||||||
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\Pages\\Settings.razor",
|
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\Pages\\Settings.razor",
|
||||||
"RelativeDocumentMoniker": "Components\\Pages\\Settings.razor",
|
"RelativeDocumentMoniker": "Components\\Pages\\Settings.razor",
|
||||||
|
|
@ -234,12 +278,11 @@
|
||||||
"RelativeToolTip": "Components\\Pages\\Settings.razor",
|
"RelativeToolTip": "Components\\Pages\\Settings.razor",
|
||||||
"ViewState": "AgIAAAwAAAAAAAAAAAAAAIYAAAAlAAAAAAAAAA==",
|
"ViewState": "AgIAAAwAAAAAAAAAAAAAAIYAAAAlAAAAAAAAAA==",
|
||||||
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000759|",
|
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000759|",
|
||||||
"WhenOpened": "2026-05-15T16:43:26.6Z",
|
"WhenOpened": "2026-05-15T16:43:26.6Z"
|
||||||
"EditorCaption": ""
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"$type": "Document",
|
"$type": "Document",
|
||||||
"DocumentIndex": 6,
|
"DocumentIndex": 9,
|
||||||
"Title": "SettingsService.cs",
|
"Title": "SettingsService.cs",
|
||||||
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\SettingsService.cs",
|
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\SettingsService.cs",
|
||||||
"RelativeDocumentMoniker": "Services\\SettingsService.cs",
|
"RelativeDocumentMoniker": "Services\\SettingsService.cs",
|
||||||
|
|
@ -247,12 +290,11 @@
|
||||||
"RelativeToolTip": "Services\\SettingsService.cs",
|
"RelativeToolTip": "Services\\SettingsService.cs",
|
||||||
"ViewState": "AgIAAEUBAAAAAAAAAAAQwFwBAAAAAAAAAAAAAA==",
|
"ViewState": "AgIAAEUBAAAAAAAAAAAQwFwBAAAAAAAAAAAAAA==",
|
||||||
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
|
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
|
||||||
"WhenOpened": "2026-05-16T06:05:12.928Z",
|
"WhenOpened": "2026-05-16T06:05:12.928Z"
|
||||||
"EditorCaption": ""
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"$type": "Document",
|
"$type": "Document",
|
||||||
"DocumentIndex": 3,
|
"DocumentIndex": 6,
|
||||||
"Title": "OllamaService.cs",
|
"Title": "OllamaService.cs",
|
||||||
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\OllamaService.cs",
|
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\OllamaService.cs",
|
||||||
"RelativeDocumentMoniker": "Services\\OllamaService.cs",
|
"RelativeDocumentMoniker": "Services\\OllamaService.cs",
|
||||||
|
|
@ -260,12 +302,11 @@
|
||||||
"RelativeToolTip": "Services\\OllamaService.cs",
|
"RelativeToolTip": "Services\\OllamaService.cs",
|
||||||
"ViewState": "AgIAAFkAAAAAAAAAAAAqwGsAAAAeAAAAAAAAAA==",
|
"ViewState": "AgIAAFkAAAAAAAAAAAAqwGsAAAAeAAAAAAAAAA==",
|
||||||
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
|
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
|
||||||
"WhenOpened": "2026-05-16T06:43:46.404Z",
|
"WhenOpened": "2026-05-16T06:43:46.404Z"
|
||||||
"EditorCaption": ""
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"$type": "Document",
|
"$type": "Document",
|
||||||
"DocumentIndex": 12,
|
"DocumentIndex": 14,
|
||||||
"Title": "RagService.cs",
|
"Title": "RagService.cs",
|
||||||
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\RagService.cs",
|
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\RagService.cs",
|
||||||
"RelativeDocumentMoniker": "Services\\RagService.cs",
|
"RelativeDocumentMoniker": "Services\\RagService.cs",
|
||||||
|
|
@ -277,7 +318,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"$type": "Document",
|
"$type": "Document",
|
||||||
"DocumentIndex": 7,
|
"DocumentIndex": 10,
|
||||||
"Title": "ISettingsService.cs",
|
"Title": "ISettingsService.cs",
|
||||||
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\ISettingsService.cs",
|
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\ISettingsService.cs",
|
||||||
"RelativeDocumentMoniker": "Services\\ISettingsService.cs",
|
"RelativeDocumentMoniker": "Services\\ISettingsService.cs",
|
||||||
|
|
@ -285,25 +326,11 @@
|
||||||
"RelativeToolTip": "Services\\ISettingsService.cs",
|
"RelativeToolTip": "Services\\ISettingsService.cs",
|
||||||
"ViewState": "AgIAABoAAAAAAAAAAAA8wCoAAAAPAAAAAAAAAA==",
|
"ViewState": "AgIAABoAAAAAAAAAAAA8wCoAAAAPAAAAAAAAAA==",
|
||||||
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
|
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
|
||||||
"WhenOpened": "2026-05-16T13:21:26.951Z",
|
"WhenOpened": "2026-05-16T13:21:26.951Z"
|
||||||
"EditorCaption": ""
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"$type": "Document",
|
"$type": "Document",
|
||||||
"DocumentIndex": 8,
|
"DocumentIndex": 15,
|
||||||
"Title": "Program.cs",
|
|
||||||
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Program.cs",
|
|
||||||
"RelativeDocumentMoniker": "Program.cs",
|
|
||||||
"ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Program.cs",
|
|
||||||
"RelativeToolTip": "Program.cs",
|
|
||||||
"ViewState": "AgIAAAMAAAAAAAAAAAApwBoAAAANAAAAAAAAAA==",
|
|
||||||
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
|
|
||||||
"WhenOpened": "2026-05-20T04:07:21.54Z",
|
|
||||||
"EditorCaption": ""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"$type": "Document",
|
|
||||||
"DocumentIndex": 13,
|
|
||||||
"Title": "IRagService.cs",
|
"Title": "IRagService.cs",
|
||||||
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\IRagService.cs",
|
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\IRagService.cs",
|
||||||
"RelativeDocumentMoniker": "Services\\IRagService.cs",
|
"RelativeDocumentMoniker": "Services\\IRagService.cs",
|
||||||
|
|
@ -315,7 +342,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"$type": "Document",
|
"$type": "Document",
|
||||||
"DocumentIndex": 9,
|
"DocumentIndex": 11,
|
||||||
"Title": "ProjectService.cs",
|
"Title": "ProjectService.cs",
|
||||||
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\ProjectService.cs",
|
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\ProjectService.cs",
|
||||||
"RelativeDocumentMoniker": "Services\\ProjectService.cs",
|
"RelativeDocumentMoniker": "Services\\ProjectService.cs",
|
||||||
|
|
@ -323,12 +350,11 @@
|
||||||
"RelativeToolTip": "Services\\ProjectService.cs",
|
"RelativeToolTip": "Services\\ProjectService.cs",
|
||||||
"ViewState": "AgIAADQAAAAAAAAAAAAqwEYAAAAWAAAAAAAAAA==",
|
"ViewState": "AgIAADQAAAAAAAAAAAAqwEYAAAAWAAAAAAAAAA==",
|
||||||
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
|
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
|
||||||
"WhenOpened": "2026-05-15T15:56:55.229Z",
|
"WhenOpened": "2026-05-15T15:56:55.229Z"
|
||||||
"EditorCaption": ""
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"$type": "Document",
|
"$type": "Document",
|
||||||
"DocumentIndex": 15,
|
"DocumentIndex": 17,
|
||||||
"Title": "NavMenu.razor",
|
"Title": "NavMenu.razor",
|
||||||
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\Layout\\NavMenu.razor",
|
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\Layout\\NavMenu.razor",
|
||||||
"RelativeDocumentMoniker": "Components\\Layout\\NavMenu.razor",
|
"RelativeDocumentMoniker": "Components\\Layout\\NavMenu.razor",
|
||||||
|
|
@ -340,7 +366,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"$type": "Document",
|
"$type": "Document",
|
||||||
"DocumentIndex": 14,
|
"DocumentIndex": 16,
|
||||||
"Title": "Editor.razor",
|
"Title": "Editor.razor",
|
||||||
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\Pages\\Editor.razor",
|
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\Pages\\Editor.razor",
|
||||||
"RelativeDocumentMoniker": "Components\\Pages\\Editor.razor",
|
"RelativeDocumentMoniker": "Components\\Pages\\Editor.razor",
|
||||||
|
|
@ -353,7 +379,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"$type": "Document",
|
"$type": "Document",
|
||||||
"DocumentIndex": 17,
|
"DocumentIndex": 19,
|
||||||
"Title": "ThemeService.cs",
|
"Title": "ThemeService.cs",
|
||||||
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\ThemeService.cs",
|
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\ThemeService.cs",
|
||||||
"RelativeDocumentMoniker": "Services\\ThemeService.cs",
|
"RelativeDocumentMoniker": "Services\\ThemeService.cs",
|
||||||
|
|
@ -365,7 +391,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"$type": "Document",
|
"$type": "Document",
|
||||||
"DocumentIndex": 18,
|
"DocumentIndex": 20,
|
||||||
"Title": "App.razor",
|
"Title": "App.razor",
|
||||||
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\App.razor",
|
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\App.razor",
|
||||||
"RelativeDocumentMoniker": "Components\\App.razor",
|
"RelativeDocumentMoniker": "Components\\App.razor",
|
||||||
|
|
@ -377,7 +403,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"$type": "Document",
|
"$type": "Document",
|
||||||
"DocumentIndex": 21,
|
"DocumentIndex": 23,
|
||||||
"Title": "Models.razor",
|
"Title": "Models.razor",
|
||||||
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\Pages\\Models.razor",
|
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\Pages\\Models.razor",
|
||||||
"RelativeDocumentMoniker": "Components\\Pages\\Models.razor",
|
"RelativeDocumentMoniker": "Components\\Pages\\Models.razor",
|
||||||
|
|
@ -389,7 +415,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"$type": "Document",
|
"$type": "Document",
|
||||||
"DocumentIndex": 19,
|
"DocumentIndex": 21,
|
||||||
"Title": "LLM_Model.cs",
|
"Title": "LLM_Model.cs",
|
||||||
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Models\\LLM_Model.cs",
|
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Models\\LLM_Model.cs",
|
||||||
"RelativeDocumentMoniker": "Models\\LLM_Model.cs",
|
"RelativeDocumentMoniker": "Models\\LLM_Model.cs",
|
||||||
|
|
@ -401,7 +427,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"$type": "Document",
|
"$type": "Document",
|
||||||
"DocumentIndex": 20,
|
"DocumentIndex": 22,
|
||||||
"Title": "ModelStatus.cs",
|
"Title": "ModelStatus.cs",
|
||||||
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Models\\ModelStatus.cs",
|
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Models\\ModelStatus.cs",
|
||||||
"RelativeDocumentMoniker": "Models\\ModelStatus.cs",
|
"RelativeDocumentMoniker": "Models\\ModelStatus.cs",
|
||||||
|
|
@ -413,7 +439,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"$type": "Document",
|
"$type": "Document",
|
||||||
"DocumentIndex": 24,
|
"DocumentIndex": 26,
|
||||||
"Title": "MainLayout.razor.css",
|
"Title": "MainLayout.razor.css",
|
||||||
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\Layout\\MainLayout.razor.css",
|
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\Layout\\MainLayout.razor.css",
|
||||||
"RelativeDocumentMoniker": "Components\\Layout\\MainLayout.razor.css",
|
"RelativeDocumentMoniker": "Components\\Layout\\MainLayout.razor.css",
|
||||||
|
|
@ -425,7 +451,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"$type": "Document",
|
"$type": "Document",
|
||||||
"DocumentIndex": 22,
|
"DocumentIndex": 24,
|
||||||
"Title": "IFileScannerService.cs",
|
"Title": "IFileScannerService.cs",
|
||||||
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\IFileScannerService.cs",
|
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\IFileScannerService.cs",
|
||||||
"RelativeDocumentMoniker": "Services\\IFileScannerService.cs",
|
"RelativeDocumentMoniker": "Services\\IFileScannerService.cs",
|
||||||
|
|
@ -437,7 +463,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"$type": "Document",
|
"$type": "Document",
|
||||||
"DocumentIndex": 23,
|
"DocumentIndex": 25,
|
||||||
"Title": "bootstrap-utilities.css",
|
"Title": "bootstrap-utilities.css",
|
||||||
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\wwwroot\\lib\\bootstrap\\dist\\css\\bootstrap-utilities.css",
|
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\wwwroot\\lib\\bootstrap\\dist\\css\\bootstrap-utilities.css",
|
||||||
"RelativeDocumentMoniker": "wwwroot\\lib\\bootstrap\\dist\\css\\bootstrap-utilities.css",
|
"RelativeDocumentMoniker": "wwwroot\\lib\\bootstrap\\dist\\css\\bootstrap-utilities.css",
|
||||||
|
|
@ -449,7 +475,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"$type": "Document",
|
"$type": "Document",
|
||||||
"DocumentIndex": 26,
|
"DocumentIndex": 28,
|
||||||
"Title": "ProjectEntity.cs",
|
"Title": "ProjectEntity.cs",
|
||||||
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Data\\ProjectEntity.cs",
|
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Data\\ProjectEntity.cs",
|
||||||
"RelativeDocumentMoniker": "Data\\ProjectEntity.cs",
|
"RelativeDocumentMoniker": "Data\\ProjectEntity.cs",
|
||||||
|
|
@ -461,7 +487,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"$type": "Document",
|
"$type": "Document",
|
||||||
"DocumentIndex": 25,
|
"DocumentIndex": 27,
|
||||||
"Title": "IProjectService.cs",
|
"Title": "IProjectService.cs",
|
||||||
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\IProjectService.cs",
|
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\IProjectService.cs",
|
||||||
"RelativeDocumentMoniker": "Services\\IProjectService.cs",
|
"RelativeDocumentMoniker": "Services\\IProjectService.cs",
|
||||||
|
|
@ -473,7 +499,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"$type": "Document",
|
"$type": "Document",
|
||||||
"DocumentIndex": 27,
|
"DocumentIndex": 29,
|
||||||
"Title": "AuthorBuddy.Web.csproj",
|
"Title": "AuthorBuddy.Web.csproj",
|
||||||
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\AuthorBuddy.Web.csproj",
|
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\AuthorBuddy.Web.csproj",
|
||||||
"RelativeDocumentMoniker": "AuthorBuddy.Web.csproj",
|
"RelativeDocumentMoniker": "AuthorBuddy.Web.csproj",
|
||||||
|
|
@ -485,7 +511,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"$type": "Document",
|
"$type": "Document",
|
||||||
"DocumentIndex": 28,
|
"DocumentIndex": 30,
|
||||||
"Title": "appsettings.json",
|
"Title": "appsettings.json",
|
||||||
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\appsettings.json",
|
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\appsettings.json",
|
||||||
"RelativeDocumentMoniker": "appsettings.json",
|
"RelativeDocumentMoniker": "appsettings.json",
|
||||||
|
|
|
||||||
|
|
@ -15,11 +15,11 @@
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<Routes @rendermode="InteractiveServer" />
|
<CascadingAuthenticationState>
|
||||||
|
<Routes @rendermode="InteractiveServer" />
|
||||||
|
</CascadingAuthenticationState>
|
||||||
<ReconnectModal />
|
<ReconnectModal />
|
||||||
<script src="@Assets["_framework/blazor.web.js"]"></script>
|
<script src="@Assets["_framework/blazor.web.js"]"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,9 +4,13 @@
|
||||||
@inject ISettingsService SettingsService
|
@inject ISettingsService SettingsService
|
||||||
@inject IThemeService ThemeService
|
@inject IThemeService ThemeService
|
||||||
@inject OllamaSettings OllamaSettings
|
@inject OllamaSettings OllamaSettings
|
||||||
|
@inject IAuthService AuthService
|
||||||
|
@inject CustomAuthStateProvider AuthStateProvider
|
||||||
|
|
||||||
<div class="page">
|
<div class="page">
|
||||||
<div class="main-content">
|
<div class="main-content">
|
||||||
|
@if (_isAuthenticated)
|
||||||
|
{
|
||||||
<aside class="sidebar">
|
<aside class="sidebar">
|
||||||
<div class="nav-section">
|
<div class="nav-section">
|
||||||
<button class="nav-section-header" @onclick='() => ToggleSection("file")'>
|
<button class="nav-section-header" @onclick='() => ToggleSection("file")'>
|
||||||
|
|
@ -150,7 +154,22 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@if (!string.IsNullOrEmpty(_username))
|
||||||
|
{
|
||||||
|
<div class="nav-section" style="border-top:1px solid var(--border-color);margin-top:8px;padding-top:4px;">
|
||||||
|
<div class="nav-section-header" style="cursor:default;">
|
||||||
|
<span style="font-size:11px;font-weight:normal;">@T("Angemeldet als:", "Logged in as:") @_username</span>
|
||||||
|
</div>
|
||||||
|
<div class="nav-section-body">
|
||||||
|
<button class="nav-item" @onclick="NavigateToSetup2FA">@T("2FA verwalten", "Manage 2FA")</button>
|
||||||
|
<button class="nav-item" @onclick="Logout">@T("Abmelden", "Logout")</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
</aside>
|
</aside>
|
||||||
|
}
|
||||||
|
|
||||||
<main class="detail-panel">
|
<main class="detail-panel">
|
||||||
@Body
|
@Body
|
||||||
|
|
@ -169,6 +188,8 @@
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
private string _language = "de";
|
private string _language = "de";
|
||||||
|
private string _username = string.Empty;
|
||||||
|
private bool _isAuthenticated;
|
||||||
private string? activeSection;
|
private string? activeSection;
|
||||||
private bool showProjectList;
|
private bool showProjectList;
|
||||||
private List<Project>? projects;
|
private List<Project>? projects;
|
||||||
|
|
@ -196,9 +217,13 @@
|
||||||
_language = await SettingsService.GetLanguageAsync() ?? "de";
|
_language = await SettingsService.GetLanguageAsync() ?? "de";
|
||||||
statusText = T("Bereit", "Ready");
|
statusText = T("Bereit", "Ready");
|
||||||
Navigation.LocationChanged += OnLocationChanged;
|
Navigation.LocationChanged += OnLocationChanged;
|
||||||
await LoadProjects();
|
}
|
||||||
await LoadProjectContext();
|
|
||||||
await AutoLoadLastProject();
|
private async Task LoadCurrentUser()
|
||||||
|
{
|
||||||
|
var user = await AuthService.GetCurrentUserAsync();
|
||||||
|
_username = user?.Name ?? string.Empty;
|
||||||
|
_isAuthenticated = user != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task AutoLoadLastProject()
|
private async Task AutoLoadLastProject()
|
||||||
|
|
@ -221,6 +246,29 @@
|
||||||
var theme = OllamaSettings.Theme;
|
var theme = OllamaSettings.Theme;
|
||||||
if (!string.IsNullOrEmpty(theme))
|
if (!string.IsNullOrEmpty(theme))
|
||||||
await ThemeService.ApplyTheme(theme);
|
await ThemeService.ApplyTheme(theme);
|
||||||
|
|
||||||
|
// Auto-login runs AFTER circuit establishment (correct scoped services)
|
||||||
|
await LoadCurrentUser();
|
||||||
|
#if DEBUG
|
||||||
|
if (!_isAuthenticated && Program.DebugAutoLogin)
|
||||||
|
{
|
||||||
|
var users = await SettingsService.GetUsersAsync();
|
||||||
|
var first = users.FirstOrDefault();
|
||||||
|
if (first != null)
|
||||||
|
{
|
||||||
|
await AuthService.AdminBypassLoginAsync(first.Id);
|
||||||
|
AuthStateProvider.NotifyStateChanged();
|
||||||
|
await LoadCurrentUser();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
if (_isAuthenticated)
|
||||||
|
{
|
||||||
|
await LoadProjects();
|
||||||
|
await LoadProjectContext();
|
||||||
|
await AutoLoadLastProject();
|
||||||
|
}
|
||||||
|
StateHasChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -231,8 +279,12 @@
|
||||||
|
|
||||||
private async void OnLocationChanged(object? sender, Microsoft.AspNetCore.Components.Routing.LocationChangedEventArgs e)
|
private async void OnLocationChanged(object? sender, Microsoft.AspNetCore.Components.Routing.LocationChangedEventArgs e)
|
||||||
{
|
{
|
||||||
await LoadProjects();
|
await LoadCurrentUser();
|
||||||
await LoadProjectContext();
|
if (_isAuthenticated)
|
||||||
|
{
|
||||||
|
await LoadProjects();
|
||||||
|
await LoadProjectContext();
|
||||||
|
}
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -304,6 +356,21 @@
|
||||||
Navigation.NavigateTo(path);
|
Navigation.NavigateTo(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void NavigateToSetup2FA()
|
||||||
|
{
|
||||||
|
activeSection = null;
|
||||||
|
Navigation.NavigateTo("/setup-2fa");
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task Logout()
|
||||||
|
{
|
||||||
|
activeSection = null;
|
||||||
|
await AuthService.LogoutAsync();
|
||||||
|
_username = string.Empty;
|
||||||
|
AuthStateProvider.NotifyStateChanged();
|
||||||
|
Navigation.NavigateTo("/login", true);
|
||||||
|
}
|
||||||
|
|
||||||
private async Task SelectProject(Project project)
|
private async Task SelectProject(Project project)
|
||||||
{
|
{
|
||||||
activeSection = "files";
|
activeSection = "files";
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@
|
||||||
|
|
||||||
@implements IDisposable
|
@implements IDisposable
|
||||||
@rendermode InteractiveServer
|
@rendermode InteractiveServer
|
||||||
|
@attribute [Authorize]
|
||||||
|
|
||||||
<PageTitle>@T("Editor - Author Buddy", "Editor - Author Buddy")</PageTitle>
|
<PageTitle>@T("Editor - Author Buddy", "Editor - Author Buddy")</PageTitle>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
@page "/"
|
@page "/"
|
||||||
|
@attribute [Authorize]
|
||||||
@inject IOllamaService OllamaService
|
@inject IOllamaService OllamaService
|
||||||
@inject IProjectService ProjectService
|
@inject IProjectService ProjectService
|
||||||
@inject ISettingsService SettingsService
|
@inject ISettingsService SettingsService
|
||||||
|
|
|
||||||
166
Components/Pages/Login.razor
Normal file
166
Components/Pages/Login.razor
Normal file
|
|
@ -0,0 +1,166 @@
|
||||||
|
@page "/login"
|
||||||
|
@inject IAuthService AuthService
|
||||||
|
@inject CustomAuthStateProvider AuthStateProvider
|
||||||
|
@inject NavigationManager Navigation
|
||||||
|
@inject ISettingsService SettingsService
|
||||||
|
|
||||||
|
@rendermode InteractiveServer
|
||||||
|
|
||||||
|
<PageTitle>@T("Anmelden - Author Buddy", "Login - Author Buddy")</PageTitle>
|
||||||
|
|
||||||
|
<div class="auth-page">
|
||||||
|
<div class="auth-card">
|
||||||
|
<h2>@T("Anmelden", "Login")</h2>
|
||||||
|
|
||||||
|
@if (!show2FA)
|
||||||
|
{
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="username">@T("Benutzername:", "Username:")</label>
|
||||||
|
<input id="username" @bind="username" @onkeydown="HandleKeyDown" />
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="password">@T("Passwort:", "Password:")</label>
|
||||||
|
<input id="password" type="password" @bind="password" @onkeydown="HandleKeyDown" />
|
||||||
|
</div>
|
||||||
|
<div class="auth-actions">
|
||||||
|
<button @onclick="DoLogin" disabled="@isBusy">@T("Anmelden", "Login")</button>
|
||||||
|
<a href="/register">@T("Registrieren", "Register")</a>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<p>@T("Gib den 6-stelligen Code aus deiner Authenticator-App ein:", "Enter the 6-digit code from your authenticator app:")</p>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="code">@T("2FA-Code:", "2FA code:")</label>
|
||||||
|
<input id="code" @bind="twoFactorCode" maxlength="6" @onkeydown="HandleKeyDown" />
|
||||||
|
</div>
|
||||||
|
<div class="auth-actions">
|
||||||
|
<button @onclick="Verify2FA" disabled="@isBusy">@T("Bestätigen", "Verify")</button>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
@if (!string.IsNullOrEmpty(errorMessage))
|
||||||
|
{
|
||||||
|
<div class="auth-error">@errorMessage</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@code {
|
||||||
|
private string _language = "de";
|
||||||
|
private string username = string.Empty;
|
||||||
|
private string password = string.Empty;
|
||||||
|
private string twoFactorCode = string.Empty;
|
||||||
|
private string errorMessage = string.Empty;
|
||||||
|
private bool isBusy;
|
||||||
|
private bool show2FA;
|
||||||
|
|
||||||
|
private string T(string de, string en) => _language == "en" ? en : de;
|
||||||
|
|
||||||
|
protected override async Task OnInitializedAsync()
|
||||||
|
{
|
||||||
|
_language = await SettingsService.GetLanguageAsync() ?? "de";
|
||||||
|
|
||||||
|
if (AuthService.IsLoggedIn)
|
||||||
|
{
|
||||||
|
Navigation.NavigateTo("/");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var users = await SettingsService.GetUsersAsync();
|
||||||
|
if (users.Count == 0)
|
||||||
|
{
|
||||||
|
Navigation.NavigateTo("/register");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||||
|
{
|
||||||
|
if (firstRender && AuthService.IsLoggedIn)
|
||||||
|
Navigation.NavigateTo("/");
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task HandleKeyDown(KeyboardEventArgs e)
|
||||||
|
{
|
||||||
|
if (e.Key == "Enter")
|
||||||
|
{
|
||||||
|
if (show2FA)
|
||||||
|
await Verify2FA();
|
||||||
|
else
|
||||||
|
await DoLogin();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task DoLogin()
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(username) || string.IsNullOrWhiteSpace(password))
|
||||||
|
{
|
||||||
|
errorMessage = T("Bitte Benutzername und Passwort eingeben.", "Please enter username and password.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
isBusy = true;
|
||||||
|
errorMessage = string.Empty;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var result = await AuthService.LoginAsync(username, password);
|
||||||
|
if (AuthService.Is2FARequired)
|
||||||
|
{
|
||||||
|
show2FA = true;
|
||||||
|
password = string.Empty;
|
||||||
|
}
|
||||||
|
else if (result)
|
||||||
|
{
|
||||||
|
AuthStateProvider.NotifyStateChanged();
|
||||||
|
Navigation.NavigateTo("/");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
errorMessage = T("Benutzername oder Passwort falsch.", "Invalid username or password.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
errorMessage = $"{T("Fehler:", "Error:")} {ex.Message}";
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
isBusy = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task Verify2FA()
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(twoFactorCode) || twoFactorCode.Length != 6)
|
||||||
|
{
|
||||||
|
errorMessage = T("Bitte gib einen gültigen 6-stelligen Code ein.", "Please enter a valid 6-digit code.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
isBusy = true;
|
||||||
|
errorMessage = string.Empty;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var result = await AuthService.Verify2FAAsync(twoFactorCode);
|
||||||
|
if (result)
|
||||||
|
{
|
||||||
|
AuthStateProvider.NotifyStateChanged();
|
||||||
|
Navigation.NavigateTo("/");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
errorMessage = T("Ungültiger Code. Bitte versuche es erneut.", "Invalid code. Please try again.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
errorMessage = $"{T("Fehler:", "Error:")} {ex.Message}";
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
isBusy = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
@page "/models"
|
@page "/models"
|
||||||
|
@attribute [Authorize]
|
||||||
@inject IOllamaService OllamaService
|
@inject IOllamaService OllamaService
|
||||||
@inject ISettingsService SettingsService
|
@inject ISettingsService SettingsService
|
||||||
@inject NavigationManager Navigation
|
@inject NavigationManager Navigation
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
@page "/assistant"
|
@page "/assistant"
|
||||||
|
@attribute [Authorize]
|
||||||
@inject IOllamaService OllamaService
|
@inject IOllamaService OllamaService
|
||||||
@inject ISettingsService SettingsService
|
@inject ISettingsService SettingsService
|
||||||
@inject NavigationManager Navigation
|
@inject NavigationManager Navigation
|
||||||
|
|
|
||||||
108
Components/Pages/Register.razor
Normal file
108
Components/Pages/Register.razor
Normal file
|
|
@ -0,0 +1,108 @@
|
||||||
|
@page "/register"
|
||||||
|
@inject IAuthService AuthService
|
||||||
|
@inject CustomAuthStateProvider AuthStateProvider
|
||||||
|
@inject NavigationManager Navigation
|
||||||
|
@inject ISettingsService SettingsService
|
||||||
|
|
||||||
|
@rendermode InteractiveServer
|
||||||
|
|
||||||
|
<PageTitle>@T("Registrieren - Author Buddy", "Register - Author Buddy")</PageTitle>
|
||||||
|
|
||||||
|
<div class="auth-page">
|
||||||
|
<div class="auth-card">
|
||||||
|
<h2>@T("Registrieren", "Register")</h2>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="username">@T("Benutzername:", "Username:")</label>
|
||||||
|
<input id="username" @bind="username" @onkeydown="HandleKeyDown" />
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="password">@T("Passwort:", "Password:")</label>
|
||||||
|
<input id="password" type="password" @bind="password" @onkeydown="HandleKeyDown" />
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="confirmPassword">@T("Passwort bestätigen:", "Confirm password:")</label>
|
||||||
|
<input id="confirmPassword" type="password" @bind="confirmPassword" @onkeydown="HandleKeyDown" />
|
||||||
|
</div>
|
||||||
|
<div class="auth-actions">
|
||||||
|
<button @onclick="DoRegister" disabled="@isBusy">@T("Registrieren", "Register")</button>
|
||||||
|
<a href="/login">@T("Zurück zum Login", "Back to login")</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@if (!string.IsNullOrEmpty(errorMessage))
|
||||||
|
{
|
||||||
|
<div class="auth-error">@errorMessage</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@code {
|
||||||
|
private string _language = "de";
|
||||||
|
private string username = string.Empty;
|
||||||
|
private string password = string.Empty;
|
||||||
|
private string confirmPassword = string.Empty;
|
||||||
|
private string errorMessage = string.Empty;
|
||||||
|
private bool isBusy;
|
||||||
|
|
||||||
|
private string T(string de, string en) => _language == "en" ? en : de;
|
||||||
|
|
||||||
|
protected override async Task OnInitializedAsync()
|
||||||
|
{
|
||||||
|
_language = await SettingsService.GetLanguageAsync() ?? "de";
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task HandleKeyDown(KeyboardEventArgs e)
|
||||||
|
{
|
||||||
|
if (e.Key == "Enter")
|
||||||
|
await DoRegister();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task DoRegister()
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(username))
|
||||||
|
{
|
||||||
|
errorMessage = T("Bitte Benutzername eingeben.", "Please enter a username.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (string.IsNullOrWhiteSpace(password))
|
||||||
|
{
|
||||||
|
errorMessage = T("Bitte Passwort eingeben.", "Please enter a password.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (password != confirmPassword)
|
||||||
|
{
|
||||||
|
errorMessage = T("Passwörter stimmen nicht überein.", "Passwords do not match.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (password.Length < 4)
|
||||||
|
{
|
||||||
|
errorMessage = T("Passwort muss mindestens 4 Zeichen lang sein.", "Password must be at least 4 characters long.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
isBusy = true;
|
||||||
|
errorMessage = string.Empty;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var result = await AuthService.RegisterAsync(username, password);
|
||||||
|
if (result)
|
||||||
|
{
|
||||||
|
AuthStateProvider.NotifyStateChanged();
|
||||||
|
Navigation.NavigateTo("/");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
errorMessage = T("Benutzername ist bereits vergeben.", "Username is already taken.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
errorMessage = $"{T("Fehler:", "Error:")} {ex.Message}";
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
isBusy = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
@page "/settings"
|
@page "/settings"
|
||||||
|
@attribute [Authorize]
|
||||||
@inject ISettingsService SettingsService
|
@inject ISettingsService SettingsService
|
||||||
@inject IThemeService ThemeService
|
@inject IThemeService ThemeService
|
||||||
@inject IOllamaService OllamaService
|
@inject IOllamaService OllamaService
|
||||||
|
|
@ -30,6 +31,10 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="settings-section" style="margin-bottom:16px;">
|
||||||
|
<button class="btn btn-secondary" @onclick='() => Navigation.NavigateTo("/setup-2fa")'>@T("2FA einrichten", "Set up 2FA")</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="settings-layout">
|
<div class="settings-layout">
|
||||||
|
|
||||||
<div class="settings-column" >
|
<div class="settings-column" >
|
||||||
|
|
|
||||||
197
Components/Pages/Setup2FA.razor
Normal file
197
Components/Pages/Setup2FA.razor
Normal file
|
|
@ -0,0 +1,197 @@
|
||||||
|
@page "/setup-2fa"
|
||||||
|
@inject IAuthService AuthService
|
||||||
|
@inject NavigationManager Navigation
|
||||||
|
@inject ISettingsService SettingsService
|
||||||
|
@inject IJSRuntime JSRuntime
|
||||||
|
|
||||||
|
@rendermode InteractiveServer
|
||||||
|
@attribute [Microsoft.AspNetCore.Authorization.Authorize]
|
||||||
|
|
||||||
|
<PageTitle>@T("2FA einrichten - Author Buddy", "2FA setup - Author Buddy")</PageTitle>
|
||||||
|
|
||||||
|
<div class="auth-page">
|
||||||
|
<div class="auth-card">
|
||||||
|
<h2>@T("Zwei-Faktor-Authentifizierung", "Two-Factor Authentication")</h2>
|
||||||
|
|
||||||
|
@if (isEnabled)
|
||||||
|
{
|
||||||
|
<div class="auth-success">
|
||||||
|
<p>@T("2FA ist aktiviert.", "2FA is enabled.")</p>
|
||||||
|
<button @onclick="Disable2FA" disabled="@isBusy">@T("2FA deaktivieren", "Disable 2FA")</button>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
else if (showSecret)
|
||||||
|
{
|
||||||
|
<p>@T("Scanne den folgenden QR-Code mit deiner Authenticator-App (z.B. Google Authenticator, Microsoft Authenticator) oder gib den geheimen Schlüssel manuell ein:", "Scan the QR code below with your authenticator app (e.g. Google Authenticator, Microsoft Authenticator) or enter the secret key manually:")</p>
|
||||||
|
|
||||||
|
<div class="qr-container">
|
||||||
|
<img src="@qrCodeDataUri" alt="QR Code" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label>@T("Geheimer Schlüssel:", "Secret key:")</label>
|
||||||
|
<div class="secret-display">
|
||||||
|
<code>@secret</code>
|
||||||
|
<button @onclick="CopySecret">@T("Kopieren", "Copy")</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="code">@T("Code zur Bestätigung eingeben:", "Enter verification code:")</label>
|
||||||
|
<input id="code" @bind="verificationCode" maxlength="6" @onkeydown="HandleKeyDown" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="auth-actions">
|
||||||
|
<button @onclick="Enable2FA" disabled="@isBusy || string.IsNullOrWhiteSpace(verificationCode)">@T("2FA aktivieren", "Enable 2FA")</button>
|
||||||
|
<button @onclick="Cancel">@T("Abbrechen", "Cancel")</button>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<p>@T("Richte die Zwei-Faktor-Authentifizierung ein, um dein Konto zusätzlich zu schützen.", "Set up two-factor authentication to add an extra layer of security to your account.")</p>
|
||||||
|
<div class="auth-actions">
|
||||||
|
<button @onclick="StartSetup">@T("2FA einrichten", "Set up 2FA")</button>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
@if (!string.IsNullOrEmpty(errorMessage))
|
||||||
|
{
|
||||||
|
<div class="auth-error">@errorMessage</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@code {
|
||||||
|
private string _language = "de";
|
||||||
|
private string secret = string.Empty;
|
||||||
|
private string verificationCode = string.Empty;
|
||||||
|
private string errorMessage = string.Empty;
|
||||||
|
private string? qrCodeDataUri;
|
||||||
|
private bool showSecret;
|
||||||
|
private bool isEnabled;
|
||||||
|
private bool isBusy;
|
||||||
|
private string? username;
|
||||||
|
|
||||||
|
private string T(string de, string en) => _language == "en" ? en : de;
|
||||||
|
|
||||||
|
protected override async Task OnInitializedAsync()
|
||||||
|
{
|
||||||
|
_language = await SettingsService.GetLanguageAsync() ?? "de";
|
||||||
|
isEnabled = await AuthService.Is2FAEnabledAsync();
|
||||||
|
var user = await AuthService.GetCurrentUserAsync();
|
||||||
|
username = user?.Name;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task HandleKeyDown(KeyboardEventArgs e)
|
||||||
|
{
|
||||||
|
if (e.Key == "Enter")
|
||||||
|
await Enable2FA();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task StartSetup()
|
||||||
|
{
|
||||||
|
isBusy = true;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
secret = await AuthService.Generate2FASecretAsync();
|
||||||
|
var uri = AuthService.Get2FAProvisioningUri(secret, username ?? "user");
|
||||||
|
qrCodeDataUri = await GenerateQRCodeDataUri(uri);
|
||||||
|
showSecret = true;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
errorMessage = $"{T("Fehler:", "Error:")} {ex.Message}";
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
isBusy = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task Enable2FA()
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(verificationCode) || verificationCode.Length != 6)
|
||||||
|
{
|
||||||
|
errorMessage = T("Bitte gib einen gültigen 6-stelligen Code ein.", "Please enter a valid 6-digit code.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!TotpHelper.VerifyCode(secret, verificationCode))
|
||||||
|
{
|
||||||
|
errorMessage = T("Code ungültig. Bitte versuche es erneut.", "Invalid code. Please try again.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
isBusy = true;
|
||||||
|
errorMessage = string.Empty;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var result = await AuthService.Enable2FAAsync(secret);
|
||||||
|
if (result)
|
||||||
|
{
|
||||||
|
isEnabled = true;
|
||||||
|
showSecret = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
errorMessage = T("Fehler beim Aktivieren von 2FA.", "Failed to enable 2FA.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
errorMessage = $"{T("Fehler:", "Error:")} {ex.Message}";
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
isBusy = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task Disable2FA()
|
||||||
|
{
|
||||||
|
isBusy = true;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var result = await AuthService.Disable2FAAsync();
|
||||||
|
if (result)
|
||||||
|
{
|
||||||
|
isEnabled = false;
|
||||||
|
showSecret = false;
|
||||||
|
secret = string.Empty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
errorMessage = $"{T("Fehler:", "Error:")} {ex.Message}";
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
isBusy = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<string?> GenerateQRCodeDataUri(string uri)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var encoded = System.Web.HttpUtility.UrlEncode(uri);
|
||||||
|
return $"https://api.qrserver.com/v1/create-qr-code/?size=200x200&data={encoded}";
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task CopySecret()
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(secret))
|
||||||
|
await JSRuntime.InvokeVoidAsync("navigator.clipboard.writeText", secret);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Cancel()
|
||||||
|
{
|
||||||
|
Navigation.NavigateTo("/");
|
||||||
|
}
|
||||||
|
}
|
||||||
14
Components/RedirectToLogin.razor
Normal file
14
Components/RedirectToLogin.razor
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
@inject NavigationManager Navigation
|
||||||
|
@inject AuthenticationStateProvider AuthStateProvider
|
||||||
|
|
||||||
|
@rendermode InteractiveServer
|
||||||
|
|
||||||
|
@code {
|
||||||
|
protected override async Task OnInitializedAsync()
|
||||||
|
{
|
||||||
|
var state = await AuthStateProvider.GetAuthenticationStateAsync();
|
||||||
|
if (state.User.Identity?.IsAuthenticated == true)
|
||||||
|
return;
|
||||||
|
Navigation.NavigateTo("/login");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,10 @@
|
||||||
<Router AppAssembly="typeof(Program).Assembly" NotFoundPage="typeof(Pages.NotFound)">
|
<Router AppAssembly="typeof(Program).Assembly" NotFoundPage="typeof(Pages.NotFound)">
|
||||||
<Found Context="routeData">
|
<Found Context="routeData">
|
||||||
<RouteView RouteData="routeData" DefaultLayout="typeof(Layout.MainLayout)" />
|
<AuthorizeRouteView RouteData="routeData" DefaultLayout="typeof(Layout.MainLayout)">
|
||||||
|
<NotAuthorized>
|
||||||
|
<RedirectToLogin />
|
||||||
|
</NotAuthorized>
|
||||||
|
</AuthorizeRouteView>
|
||||||
<FocusOnNavigate RouteData="routeData" Selector="h1" />
|
<FocusOnNavigate RouteData="routeData" Selector="h1" />
|
||||||
</Found>
|
</Found>
|
||||||
</Router>
|
</Router>
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,8 @@
|
||||||
@using Microsoft.AspNetCore.Components.Web
|
@using Microsoft.AspNetCore.Components.Web
|
||||||
@using static Microsoft.AspNetCore.Components.Web.RenderMode
|
@using static Microsoft.AspNetCore.Components.Web.RenderMode
|
||||||
@using Microsoft.AspNetCore.Components.Web.Virtualization
|
@using Microsoft.AspNetCore.Components.Web.Virtualization
|
||||||
|
@using Microsoft.AspNetCore.Authorization
|
||||||
|
@using Microsoft.AspNetCore.Components.Authorization
|
||||||
@using Microsoft.JSInterop
|
@using Microsoft.JSInterop
|
||||||
@using AuthorBuddy.Web
|
@using AuthorBuddy.Web
|
||||||
@using AuthorBuddy.Web.Components
|
@using AuthorBuddy.Web.Components
|
||||||
|
|
|
||||||
|
|
@ -5,4 +5,7 @@ public class UserEntity
|
||||||
public int Id { get; set; }
|
public int Id { get; set; }
|
||||||
public string Name { get; set; } = string.Empty;
|
public string Name { get; set; } = string.Empty;
|
||||||
public bool IsCurrent { get; set; }
|
public bool IsCurrent { get; set; }
|
||||||
|
public string PasswordHash { get; set; } = string.Empty;
|
||||||
|
public string TwoFactorSecret { get; set; } = string.Empty;
|
||||||
|
public bool TwoFactorEnabled { get; set; }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
24
Program.cs
24
Program.cs
|
|
@ -1,8 +1,9 @@
|
||||||
using AuthorBuddy.Web.Components;
|
using AuthorBuddy.Web.Components;
|
||||||
using AuthorBuddy.Web.Data;
|
|
||||||
using AuthorBuddy.Web.Models;
|
using AuthorBuddy.Web.Models;
|
||||||
using AuthorBuddy.Web.Services;
|
using AuthorBuddy.Web.Services;
|
||||||
using LiteDB;
|
using LiteDB;
|
||||||
|
using Microsoft.AspNetCore.Authentication.Cookies;
|
||||||
|
using Microsoft.AspNetCore.Components.Authorization;
|
||||||
|
|
||||||
var builder = WebApplication.CreateBuilder(args);
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
|
|
||||||
|
|
@ -20,6 +21,18 @@ builder.Services.AddScoped<IThemeService, ThemeService>();
|
||||||
builder.Services.AddSingleton<IProjectService, ProjectService>();
|
builder.Services.AddSingleton<IProjectService, ProjectService>();
|
||||||
builder.Services.AddSingleton<IRagService, RagService>();
|
builder.Services.AddSingleton<IRagService, RagService>();
|
||||||
builder.Services.AddSingleton<IWikipediaService, WikipediaService>();
|
builder.Services.AddSingleton<IWikipediaService, WikipediaService>();
|
||||||
|
builder.Services.AddScoped<IAuthService, AuthService>();
|
||||||
|
builder.Services.AddScoped<CustomAuthStateProvider>();
|
||||||
|
builder.Services.AddScoped<AuthenticationStateProvider>(sp =>
|
||||||
|
sp.GetRequiredService<CustomAuthStateProvider>());
|
||||||
|
builder.Services.AddAuthorizationCore();
|
||||||
|
builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
|
||||||
|
.AddCookie(options =>
|
||||||
|
{
|
||||||
|
options.LoginPath = "/login";
|
||||||
|
options.Cookie.HttpOnly = true;
|
||||||
|
options.ExpireTimeSpan = TimeSpan.FromDays(30);
|
||||||
|
});
|
||||||
|
|
||||||
var app = builder.Build();
|
var app = builder.Build();
|
||||||
|
|
||||||
|
|
@ -30,6 +43,10 @@ if (!app.Environment.IsDevelopment())
|
||||||
{
|
{
|
||||||
app.UseExceptionHandler("/Error", createScopeForErrors: true);
|
app.UseExceptionHandler("/Error", createScopeForErrors: true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
app.UseStatusCodePagesWithReExecute("/not-found", createScopeForStatusCodePages: true);
|
app.UseStatusCodePagesWithReExecute("/not-found", createScopeForStatusCodePages: true);
|
||||||
app.UseAntiforgery();
|
app.UseAntiforgery();
|
||||||
|
|
||||||
|
|
@ -38,3 +55,8 @@ app.MapRazorComponents<App>()
|
||||||
.AddInteractiveServerRenderMode();
|
.AddInteractiveServerRenderMode();
|
||||||
|
|
||||||
app.Run();
|
app.Run();
|
||||||
|
|
||||||
|
public partial class Program
|
||||||
|
{
|
||||||
|
public const bool DebugAutoLogin = true;
|
||||||
|
}
|
||||||
|
|
|
||||||
176
Services/AuthService.cs
Normal file
176
Services/AuthService.cs
Normal file
|
|
@ -0,0 +1,176 @@
|
||||||
|
using AuthorBuddy.Web.Data;
|
||||||
|
using LiteDB;
|
||||||
|
|
||||||
|
namespace AuthorBuddy.Web.Services;
|
||||||
|
|
||||||
|
public class AuthService : IAuthService
|
||||||
|
{
|
||||||
|
private readonly ILiteDatabase _db;
|
||||||
|
private readonly ISettingsService _settingsService;
|
||||||
|
private UserEntity? _currentUser;
|
||||||
|
private string? _pending2FASecret;
|
||||||
|
private bool _requires2FA;
|
||||||
|
|
||||||
|
public AuthService(ILiteDatabase db, ISettingsService settingsService)
|
||||||
|
{
|
||||||
|
_db = db;
|
||||||
|
_settingsService = settingsService;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsLoggedIn => _currentUser != null;
|
||||||
|
public bool Is2FARequired => _requires2FA;
|
||||||
|
|
||||||
|
public async Task<bool> LoginAsync(string username, string password)
|
||||||
|
{
|
||||||
|
var col = _db.GetCollection<UserEntity>("users");
|
||||||
|
var user = col.FindOne(u => u.Name == username);
|
||||||
|
|
||||||
|
if (user == null || string.IsNullOrEmpty(user.PasswordHash))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!TotpHelper.VerifyPassword(password, user.PasswordHash))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (user.TwoFactorEnabled)
|
||||||
|
{
|
||||||
|
_pending2FASecret = user.TwoFactorSecret;
|
||||||
|
_requires2FA = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
await SetAuthenticatedUser(user);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task LogoutAsync()
|
||||||
|
{
|
||||||
|
_currentUser = null;
|
||||||
|
_requires2FA = false;
|
||||||
|
_pending2FASecret = null;
|
||||||
|
await Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task AdminBypassLoginAsync(int userId)
|
||||||
|
{
|
||||||
|
var col = _db.GetCollection<UserEntity>("users");
|
||||||
|
var user = col.FindById(userId);
|
||||||
|
if (user != null)
|
||||||
|
await SetAuthenticatedUser(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<bool> RegisterAsync(string username, string password)
|
||||||
|
{
|
||||||
|
var col = _db.GetCollection<UserEntity>("users");
|
||||||
|
var existing = col.FindOne(u => u.Name == username);
|
||||||
|
if (existing != null) return false;
|
||||||
|
|
||||||
|
var user = new UserEntity
|
||||||
|
{
|
||||||
|
Name = username,
|
||||||
|
PasswordHash = TotpHelper.HashPassword(password),
|
||||||
|
IsCurrent = true,
|
||||||
|
TwoFactorEnabled = false,
|
||||||
|
TwoFactorSecret = string.Empty
|
||||||
|
};
|
||||||
|
col.Insert(user);
|
||||||
|
await SetAuthenticatedUser(user);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<UserEntity?> GetCurrentUserAsync()
|
||||||
|
{
|
||||||
|
return Task.FromResult(_currentUser);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<bool> Verify2FAAsync(string code)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(_pending2FASecret)) return false;
|
||||||
|
|
||||||
|
if (!TotpHelper.VerifyCode(_pending2FASecret, code))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var col = _db.GetCollection<UserEntity>("users");
|
||||||
|
var user = col.FindOne(u => u.TwoFactorSecret == _pending2FASecret);
|
||||||
|
if (user == null) return false;
|
||||||
|
|
||||||
|
await SetAuthenticatedUser(user);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<string> Generate2FASecretAsync()
|
||||||
|
{
|
||||||
|
return Task.FromResult(TotpHelper.GenerateSecret());
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Get2FAProvisioningUri(string secret, string username)
|
||||||
|
{
|
||||||
|
return TotpHelper.GetProvisioningUri(secret, username);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<bool> Enable2FAAsync(string secret)
|
||||||
|
{
|
||||||
|
if (_currentUser == null) return false;
|
||||||
|
|
||||||
|
var col = _db.GetCollection<UserEntity>("users");
|
||||||
|
var user = col.FindById(_currentUser.Id);
|
||||||
|
if (user == null) return false;
|
||||||
|
|
||||||
|
user.TwoFactorSecret = secret;
|
||||||
|
user.TwoFactorEnabled = true;
|
||||||
|
col.Update(user);
|
||||||
|
_currentUser.TwoFactorSecret = secret;
|
||||||
|
_currentUser.TwoFactorEnabled = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<bool> Disable2FAAsync()
|
||||||
|
{
|
||||||
|
if (_currentUser == null) return false;
|
||||||
|
|
||||||
|
var col = _db.GetCollection<UserEntity>("users");
|
||||||
|
var user = col.FindById(_currentUser.Id);
|
||||||
|
if (user == null) return false;
|
||||||
|
|
||||||
|
user.TwoFactorSecret = string.Empty;
|
||||||
|
user.TwoFactorEnabled = false;
|
||||||
|
col.Update(user);
|
||||||
|
_currentUser.TwoFactorSecret = string.Empty;
|
||||||
|
_currentUser.TwoFactorEnabled = false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<bool> Is2FAEnabledAsync()
|
||||||
|
{
|
||||||
|
if (_currentUser == null) return false;
|
||||||
|
var col = _db.GetCollection<UserEntity>("users");
|
||||||
|
var user = col.FindById(_currentUser.Id);
|
||||||
|
return user?.TwoFactorEnabled ?? false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<string?> GetCurrent2FASecretAsync()
|
||||||
|
{
|
||||||
|
if (_currentUser == null) return null;
|
||||||
|
var col = _db.GetCollection<UserEntity>("users");
|
||||||
|
var user = col.FindById(_currentUser.Id);
|
||||||
|
return user?.TwoFactorSecret;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task SetAuthenticatedUser(UserEntity user)
|
||||||
|
{
|
||||||
|
_currentUser = user;
|
||||||
|
_requires2FA = false;
|
||||||
|
_pending2FASecret = null;
|
||||||
|
_settingsService.CurrentProjectId = null;
|
||||||
|
_settingsService.CurrentFilePath = null;
|
||||||
|
|
||||||
|
var col = _db.GetCollection<UserEntity>("users");
|
||||||
|
var all = col.FindAll().ToList();
|
||||||
|
foreach (var u in all)
|
||||||
|
{
|
||||||
|
u.IsCurrent = u.Id == user.Id;
|
||||||
|
col.Update(u);
|
||||||
|
}
|
||||||
|
|
||||||
|
await _settingsService.InitializeAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
36
Services/CustomAuthStateProvider.cs
Normal file
36
Services/CustomAuthStateProvider.cs
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
using System.Security.Claims;
|
||||||
|
using Microsoft.AspNetCore.Components.Authorization;
|
||||||
|
|
||||||
|
namespace AuthorBuddy.Web.Services;
|
||||||
|
|
||||||
|
public class CustomAuthStateProvider : AuthenticationStateProvider
|
||||||
|
{
|
||||||
|
private readonly IAuthService _authService;
|
||||||
|
private AuthenticationState _anonymous;
|
||||||
|
|
||||||
|
public CustomAuthStateProvider(IAuthService authService)
|
||||||
|
{
|
||||||
|
_authService = authService;
|
||||||
|
_anonymous = new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async Task<AuthenticationState> GetAuthenticationStateAsync()
|
||||||
|
{
|
||||||
|
var user = await _authService.GetCurrentUserAsync();
|
||||||
|
if (user != null)
|
||||||
|
{
|
||||||
|
var identity = new ClaimsIdentity(new[]
|
||||||
|
{
|
||||||
|
new Claim(ClaimTypes.Name, user.Name),
|
||||||
|
new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()),
|
||||||
|
}, "custom");
|
||||||
|
return new AuthenticationState(new ClaimsPrincipal(identity));
|
||||||
|
}
|
||||||
|
return _anonymous;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void NotifyStateChanged()
|
||||||
|
{
|
||||||
|
NotifyAuthenticationStateChanged(GetAuthenticationStateAsync());
|
||||||
|
}
|
||||||
|
}
|
||||||
21
Services/IAuthService.cs
Normal file
21
Services/IAuthService.cs
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
using AuthorBuddy.Web.Data;
|
||||||
|
|
||||||
|
namespace AuthorBuddy.Web.Services;
|
||||||
|
|
||||||
|
public interface IAuthService
|
||||||
|
{
|
||||||
|
Task<bool> LoginAsync(string username, string password);
|
||||||
|
Task LogoutAsync();
|
||||||
|
Task<bool> RegisterAsync(string username, string password);
|
||||||
|
Task AdminBypassLoginAsync(int userId);
|
||||||
|
Task<UserEntity?> GetCurrentUserAsync();
|
||||||
|
bool IsLoggedIn { get; }
|
||||||
|
bool Is2FARequired { get; }
|
||||||
|
Task<bool> Verify2FAAsync(string code);
|
||||||
|
Task<string> Generate2FASecretAsync();
|
||||||
|
string Get2FAProvisioningUri(string secret, string username);
|
||||||
|
Task<bool> Enable2FAAsync(string secret);
|
||||||
|
Task<bool> Disable2FAAsync();
|
||||||
|
Task<bool> Is2FAEnabledAsync();
|
||||||
|
Task<string?> GetCurrent2FASecretAsync();
|
||||||
|
}
|
||||||
|
|
@ -280,18 +280,14 @@ public class SettingsService : ISettingsService
|
||||||
var settings = _db.GetCollection<SettingEntity>("settings");
|
var settings = _db.GetCollection<SettingEntity>("settings");
|
||||||
|
|
||||||
var userExists = users.FindAll().Any();
|
var userExists = users.FindAll().Any();
|
||||||
if (!userExists)
|
if (userExists)
|
||||||
{
|
|
||||||
var defaultUser = new UserEntity { Name = "Standardbenutzer", IsCurrent = true };
|
|
||||||
users.Insert(defaultUser);
|
|
||||||
_currentUserId = defaultUser.Id;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
var current = users.FindOne(u => u.IsCurrent);
|
var current = users.FindOne(u => u.IsCurrent);
|
||||||
_currentUserId = current?.Id ?? users.FindAll().First().Id;
|
_currentUserId = current?.Id ?? users.FindAll().First().Id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_currentUserId == null) return Task.CompletedTask;
|
||||||
|
|
||||||
var lang = _ollamaConfig.Language;
|
var lang = _ollamaConfig.Language;
|
||||||
|
|
||||||
var entries = new (string Key, string Default)[]
|
var entries = new (string Key, string Default)[]
|
||||||
|
|
|
||||||
158
Services/TotpHelper.cs
Normal file
158
Services/TotpHelper.cs
Normal file
|
|
@ -0,0 +1,158 @@
|
||||||
|
using System.Security.Cryptography;
|
||||||
|
|
||||||
|
namespace AuthorBuddy.Web.Services;
|
||||||
|
|
||||||
|
public static class TotpHelper
|
||||||
|
{
|
||||||
|
private const int TotpSize = 6;
|
||||||
|
private const int TotpStepSeconds = 30;
|
||||||
|
private const int WindowSize = 1;
|
||||||
|
|
||||||
|
public static string GenerateSecret()
|
||||||
|
{
|
||||||
|
var secret = RandomNumberGenerator.GetBytes(20);
|
||||||
|
return Base32Encode(secret);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string GetProvisioningUri(string secret, string username, string issuer = "AuthorBuddy")
|
||||||
|
{
|
||||||
|
return $"otpauth://totp/{Uri.EscapeDataString(issuer)}:{Uri.EscapeDataString(username)}" +
|
||||||
|
$"?secret={secret}&issuer={Uri.EscapeDataString(issuer)}&algorithm=SHA1&digits={TotpSize}&period={TotpStepSeconds}";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool VerifyCode(string secret, string code)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(code) || code.Length != TotpSize) return false;
|
||||||
|
if (!int.TryParse(code, out _)) return false;
|
||||||
|
|
||||||
|
var secretBytes = Base32Decode(secret);
|
||||||
|
var counter = GetCurrentCounter();
|
||||||
|
|
||||||
|
for (int i = -WindowSize; i <= WindowSize; i++)
|
||||||
|
{
|
||||||
|
var expected = GenerateTotp(secretBytes, counter + i);
|
||||||
|
if (expected == code) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static long GetCurrentCounter()
|
||||||
|
{
|
||||||
|
var unix = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
|
||||||
|
return unix / TotpStepSeconds;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GenerateTotp(byte[] secret, long counter)
|
||||||
|
{
|
||||||
|
var counterBytes = BitConverter.GetBytes(counter);
|
||||||
|
if (BitConverter.IsLittleEndian) Array.Reverse(counterBytes);
|
||||||
|
|
||||||
|
byte[] hash;
|
||||||
|
using (var hmac = new HMACSHA1(secret))
|
||||||
|
{
|
||||||
|
hash = hmac.ComputeHash(counterBytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
int offset = hash[^1] & 0xf;
|
||||||
|
int binary = ((hash[offset] & 0x7f) << 24) |
|
||||||
|
((hash[offset + 1] & 0xff) << 16) |
|
||||||
|
((hash[offset + 2] & 0xff) << 8) |
|
||||||
|
(hash[offset + 3] & 0xff);
|
||||||
|
|
||||||
|
int otp = binary % (int)Math.Pow(10, TotpSize);
|
||||||
|
return otp.ToString($"D{TotpSize}");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string Base32Encode(byte[] data)
|
||||||
|
{
|
||||||
|
const string alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
|
||||||
|
int bitCount = 0;
|
||||||
|
int currentByte = 0;
|
||||||
|
var result = new System.Text.StringBuilder();
|
||||||
|
|
||||||
|
foreach (var b in data)
|
||||||
|
{
|
||||||
|
currentByte = (currentByte << 8) | b;
|
||||||
|
bitCount += 8;
|
||||||
|
|
||||||
|
while (bitCount >= 5)
|
||||||
|
{
|
||||||
|
bitCount -= 5;
|
||||||
|
int index = (currentByte >> bitCount) & 0x1f;
|
||||||
|
result.Append(alphabet[index]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bitCount > 0)
|
||||||
|
{
|
||||||
|
int index = (currentByte << (5 - bitCount)) & 0x1f;
|
||||||
|
result.Append(alphabet[index]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static byte[] Base32Decode(string base32)
|
||||||
|
{
|
||||||
|
const string alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
|
||||||
|
base32 = base32.Trim().ToUpperInvariant()
|
||||||
|
.Replace('0', 'O').Replace('1', 'I').Replace('8', 'B');
|
||||||
|
|
||||||
|
int bitCount = 0;
|
||||||
|
int currentByte = 0;
|
||||||
|
var bytes = new System.Collections.Generic.List<byte>();
|
||||||
|
|
||||||
|
foreach (var c in base32)
|
||||||
|
{
|
||||||
|
int index = alphabet.IndexOf(c);
|
||||||
|
if (index < 0) continue;
|
||||||
|
|
||||||
|
currentByte = (currentByte << 5) | index;
|
||||||
|
bitCount += 5;
|
||||||
|
|
||||||
|
if (bitCount >= 8)
|
||||||
|
{
|
||||||
|
bitCount -= 8;
|
||||||
|
bytes.Add((byte)((currentByte >> bitCount) & 0xff));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return [.. bytes];
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string HashPassword(string password)
|
||||||
|
{
|
||||||
|
var salt = RandomNumberGenerator.GetBytes(16);
|
||||||
|
var hash = Rfc2898DeriveBytes.Pbkdf2(
|
||||||
|
password,
|
||||||
|
salt,
|
||||||
|
100_000,
|
||||||
|
HashAlgorithmName.SHA256,
|
||||||
|
32);
|
||||||
|
return $"{Convert.ToBase64String(salt)}.{Convert.ToBase64String(hash)}";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool VerifyPassword(string password, string hashedPassword)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(hashedPassword)) return false;
|
||||||
|
var parts = hashedPassword.Split('.');
|
||||||
|
if (parts.Length != 2) return false;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var salt = Convert.FromBase64String(parts[0]);
|
||||||
|
var storedHash = Convert.FromBase64String(parts[1]);
|
||||||
|
var computedHash = Rfc2898DeriveBytes.Pbkdf2(
|
||||||
|
password,
|
||||||
|
salt,
|
||||||
|
100_000,
|
||||||
|
HashAlgorithmName.SHA256,
|
||||||
|
32);
|
||||||
|
return CryptographicOperations.FixedTimeEquals(storedHash, computedHash);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue