[optimize] Phase 1 Architecture Review

This commit is contained in:
acite
2025-09-28 22:03:15 +08:00
parent c8d9e4f9ee
commit 52c18212a4
36 changed files with 514 additions and 362 deletions

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="SqlDialectMappings">
<file url="file://$PROJECT_DIR$/Abyss/Components/Services/ResourceDatabaseService.cs" dialect="GenericSQL" />
<file url="file://$PROJECT_DIR$/Abyss/Components/Services/Media/ResourceDatabaseService.cs" dialect="GenericSQL" />
</component>
</project>

View File

@@ -9,7 +9,43 @@
<option name="autoReloadType" value="SELECTIVE" />
</component>
<component name="ChangeListManager">
<list default="true" id="bf317275-3039-49bb-a475-725a800a0cce" name="Changes" comment="" />
<list default="true" id="bf317275-3039-49bb-a475-725a800a0cce" name="Changes" comment="">
<change afterPath="$PROJECT_DIR$/Abyss/Components/Services/Media/ComicService.cs" afterDir="false" />
<change afterPath="$PROJECT_DIR$/Abyss/Components/Services/Media/VideoService.cs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/.idea.Abyss/.idea/sqldialects.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/.idea.Abyss/.idea/sqldialects.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/.idea.Abyss/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/.idea.Abyss/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Abyss.sln.DotSettings.user" beforeDir="false" afterPath="$PROJECT_DIR$/Abyss.sln.DotSettings.user" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Abyss/Components/Controllers/AbyssController.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Abyss/Components/Controllers/AbyssController.cs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Abyss/Components/Controllers/Media/ImageController.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Abyss/Components/Controllers/Media/ImageController.cs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Abyss/Components/Controllers/Media/LiveController.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Abyss/Components/Controllers/Media/LiveController.cs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Abyss/Components/Controllers/Media/VideoController.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Abyss/Components/Controllers/Media/VideoController.cs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Abyss/Components/Controllers/Security/RootController.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Abyss/Components/Controllers/Security/RootController.cs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Abyss/Components/Controllers/Security/UserController.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Abyss/Components/Controllers/Security/UserController.cs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Abyss/Components/Controllers/Task/TaskController.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Abyss/Components/Controllers/Task/TaskController.cs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Abyss/Components/Services/AbyssService.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Abyss/Components/Services/Security/AbyssService.cs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Abyss/Components/Services/ConfigureService.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Abyss/Components/Services/Misc/ConfigureService.cs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Abyss/Components/Services/IndexService.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Abyss/Components/Services/Media/IndexService.cs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Abyss/Components/Services/ResourceDatabaseService.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Abyss/Components/Services/Media/ResourceDatabaseService.cs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Abyss/Components/Services/ResourceService.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Abyss/Components/Services/Media/ResourceService.cs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Abyss/Components/Services/TaskService.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Abyss/Components/Services/Media/TaskService.cs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Abyss/Components/Services/UserService.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Abyss/Components/Services/Security/UserService.cs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Abyss/Components/Static/ControllerExtensions.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Abyss/Components/Static/ControllerExtensions.cs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Abyss/Components/Tools/AbyssStream.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Abyss/Components/Tools/AbyssStream.cs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Abyss/Misc/StringClusterer.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Abyss/Misc/StringClusterer.cs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Abyss/Model/Bookmark.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Abyss/Model/Media/Bookmark.cs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Abyss/Model/ChallengeResponse.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Abyss/Model/Security/ChallengeResponse.cs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Abyss/Model/Chip.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Abyss/Model/Media/Chip.cs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Abyss/Model/Comic.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Abyss/Model/Media/Comic.cs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Abyss/Model/Comment.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Abyss/Model/Media/Comment.cs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Abyss/Model/Index.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Abyss/Model/Media/Index.cs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Abyss/Model/ResourceAttribute.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Abyss/Model/Media/ResourceAttribute.cs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Abyss/Model/Task.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Abyss/Model/Media/Task.cs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Abyss/Model/TaskCreation.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Abyss/Model/Media/TaskCreation.cs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Abyss/Model/User.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Abyss/Model/Security/User.cs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Abyss/Model/UserCreating.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Abyss/Model/Security/UserCreating.cs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Abyss/Model/Video.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Abyss/Model/Media/Video.cs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Abyss/Program.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Abyss/Program.cs" afterDir="false" />
</list>
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
@@ -41,36 +77,32 @@
<setting file="file://$PROJECT_DIR$/Abyss/Components/Controllers/Security/RootController.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Abyss/Components/Controllers/Security/UserController.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Abyss/Components/Controllers/Task/TaskController.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Abyss/Components/Services/AbyssService.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Abyss/Components/Services/ConfigureService.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Abyss/Components/Services/IndexService.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Abyss/Components/Services/ResourceDatabaseService.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Abyss/Components/Services/ResourceService.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Abyss/Components/Services/TaskService.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Abyss/Components/Services/UserService.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Abyss/Components/Services/Media/ComicService.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Abyss/Components/Services/Media/IndexService.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Abyss/Components/Services/Media/ResourceDatabaseService.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Abyss/Components/Services/Media/ResourceService.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Abyss/Components/Services/Media/TaskService.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Abyss/Components/Services/Media/VideoService.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Abyss/Components/Services/Misc/ConfigureService.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Abyss/Components/Services/Security/AbyssService.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Abyss/Components/Services/Security/UserService.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Abyss/Components/Static/Helpers.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Abyss/Components/Tools/AbyssStream.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Abyss/Components/Tools/HttpHelper.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Abyss/Components/Tools/HttpReader.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Abyss/Misc/StringClusterer.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Abyss/Model/Bookmark.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Abyss/Model/ChallengeResponse.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Abyss/Model/Chip.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Abyss/Model/Comic.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Abyss/Model/Comment.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Abyss/Model/Index.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Abyss/Model/ResourceAttribute.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Abyss/Model/Task.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Abyss/Model/TaskCreation.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Abyss/Model/User.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Abyss/Model/UserCreating.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Abyss/Model/Video.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="mock:///home/acite/AciteProjects/Abyss/Abyss/Program.cs" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///home/acite/AciteProjects/Abyss/Abyss/Program.cs" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///home/acite/AciteProjects/Abyss/Abyss/Program.cs" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///home/acite/AciteProjects/Abyss/Abyss/Program.cs" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///home/acite/AciteProjects/Abyss/Abyss/Program.cs" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///home/acite/AciteProjects/Abyss/Abyss/Program.cs" root0="SKIP_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Abyss/Model/Media/Bookmark.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Abyss/Model/Media/Chip.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Abyss/Model/Media/Comic.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Abyss/Model/Media/Comment.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Abyss/Model/Media/Index.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Abyss/Model/Media/ResourceAttribute.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Abyss/Model/Media/Task.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Abyss/Model/Media/TaskCreation.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Abyss/Model/Media/Video.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Abyss/Model/Security/ChallengeResponse.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Abyss/Model/Security/User.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Abyss/Model/Security/UserCreating.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file:///usr/lib/dotnet/sdk/9.0.109/Sdks/Microsoft.NET.Sdk/targets/Microsoft.NET.Sdk.targets" root0="FORCE_HIGHLIGHTING" />
</component>
<component name="MetaFilesCheckinStateConfiguration" checkMetaFiles="true" />
@@ -85,30 +117,30 @@
<option name="hideEmptyMiddlePackages" value="true" />
<option name="showLibraryContents" value="true" />
</component>
<component name="PropertiesComponent">{
&quot;keyToString&quot;: {
&quot;.NET Launch Settings Profile.Abyss: http.executor&quot;: &quot;Debug&quot;,
&quot;.NET Launch Settings Profile.Abyss: https.executor&quot;: &quot;Debug&quot;,
&quot;.NET Project.AbyssCli.executor&quot;: &quot;Run&quot;,
&quot;ASKED_SHARE_PROJECT_CONFIGURATION_FILES&quot;: &quot;true&quot;,
&quot;ModuleVcsDetector.initialDetectionPerformed&quot;: &quot;true&quot;,
&quot;Publish to folder.Publish Abyss to folder x86.executor&quot;: &quot;Run&quot;,
&quot;Publish to folder.Publish Abyss to folder.executor&quot;: &quot;Run&quot;,
&quot;RunOnceActivity.ShowReadmeOnStart&quot;: &quot;true&quot;,
&quot;RunOnceActivity.TerminalTabsStorage.copyFrom.TerminalArrangementManager.252&quot;: &quot;true&quot;,
&quot;RunOnceActivity.git.unshallow&quot;: &quot;true&quot;,
&quot;XThreadsFramesViewSplitterKey&quot;: &quot;0.55813956&quot;,
&quot;git-widget-placeholder&quot;: &quot;main&quot;,
&quot;last_opened_file_path&quot;: &quot;/home/acite/embd/WebProjects/Abyss/README.md&quot;,
&quot;node.js.detected.package.eslint&quot;: &quot;true&quot;,
&quot;node.js.detected.package.tslint&quot;: &quot;true&quot;,
&quot;node.js.selected.package.eslint&quot;: &quot;(autodetect)&quot;,
&quot;node.js.selected.package.tslint&quot;: &quot;(autodetect)&quot;,
&quot;nodejs_package_manager_path&quot;: &quot;npm&quot;,
&quot;settings.editor.selected.configurable&quot;: &quot;com.jetbrains.python.configuration.PyActiveSdkModuleConfigurable&quot;,
&quot;vue.rearranger.settings.migration&quot;: &quot;true&quot;
<component name="PropertiesComponent"><![CDATA[{
"keyToString": {
".NET Launch Settings Profile.Abyss: http.executor": "Run",
".NET Launch Settings Profile.Abyss: https.executor": "Debug",
".NET Project.AbyssCli.executor": "Run",
"ASKED_SHARE_PROJECT_CONFIGURATION_FILES": "true",
"ModuleVcsDetector.initialDetectionPerformed": "true",
"Publish to folder.Publish Abyss to folder x86.executor": "Run",
"Publish to folder.Publish Abyss to folder.executor": "Run",
"RunOnceActivity.ShowReadmeOnStart": "true",
"RunOnceActivity.TerminalTabsStorage.copyFrom.TerminalArrangementManager.252": "true",
"RunOnceActivity.git.unshallow": "true",
"XThreadsFramesViewSplitterKey": "0.55813956",
"git-widget-placeholder": "main",
"last_opened_file_path": "/home/acite/embd/WebProjects/Abyss/README.md",
"node.js.detected.package.eslint": "true",
"node.js.detected.package.tslint": "true",
"node.js.selected.package.eslint": "(autodetect)",
"node.js.selected.package.tslint": "(autodetect)",
"nodejs_package_manager_path": "npm",
"settings.editor.selected.configurable": "com.jetbrains.python.configuration.PyActiveSdkModuleConfigurable",
"vue.rearranger.settings.migration": "true"
}
}</component>
}]]></component>
<component name="RunManager" selected="Publish to folder.Publish Abyss to folder">
<configuration name="Publish Abyss to folder x86" type="DotNetFolderPublish" factoryName="Publish to folder">
<riderPublish configuration="Release" platform="Any CPU" produce_single_file="true" ready_to_run="true" self_contained="true" target_folder="/opt/security/https/server" target_framework="net9.0" uuid_high="3690631506471504162" uuid_low="-4858628519588143325">
@@ -246,6 +278,8 @@
<workItem from="1758814543368" duration="642000" />
<workItem from="1758815224532" duration="430000" />
<workItem from="1758905391249" duration="128000" />
<workItem from="1758906781361" duration="252000" />
<workItem from="1759036019712" duration="20077000" />
</task>
<servers />
</component>

View File

@@ -1,6 +1,7 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AAsyncTableQuery_00601_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2025_002E2_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F61fe11e9d86b4d2a9bd2b806929b7d381a400_003Fa1_003F62750ee4_003FAsyncTableQuery_00601_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AConfiguredValueTaskAwaitable_00601_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2025_002E2_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F457530be4752476295767457c3639889d1a000_003F25_003F817def70_003FConfiguredValueTaskAwaitable_00601_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AControllerBase_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2025_002E2_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F5df2accb46d040ccbbbe8331bf4d24b61daa00_003Fdf_003F93debd37_003FControllerBase_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AControllerBase_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2025_002E2_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F5df2accb46d040ccbbbe8331bf4d24b61daa00_003Fdf_003F93debd37_003FControllerBase_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AExceptionDispatchInfo_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2025_002E2_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F457530be4752476295767457c3639889d1a000_003Faf_003Faac0eaa5_003FExceptionDispatchInfo_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AKey_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2025_002E2_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Ff09ccaeb94c34c2299acd3efee0facee1a400_003F81_003F137b58b4_003FKey_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>

View File

@@ -1,4 +1,5 @@
using Abyss.Components.Services;
using Abyss.Components.Services.Misc;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Caching.Memory;

View File

@@ -1,100 +1,56 @@
using Abyss.Components.Services;
using Abyss.Components.Services.Media;
using Abyss.Components.Static;
using Abyss.Components.Tools;
using Abyss.Model;
using Abyss.Model.Media;
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace Abyss.Components.Controllers.Media;
using System.IO;
using Task = System.Threading.Tasks.Task;
[ApiController]
[Route("api/[controller]")]
public class ImageController(ResourceService rs, ConfigureService config) : BaseController
public class ImageController(ComicService comicService) : BaseController
{
public readonly string ImageFolder = Path.Combine(config.MediaRoot, "Images");
[HttpPost("init")]
public async Task<IActionResult> InitAsync(string token, string owner)
{
var r = await rs.Initialize(ImageFolder, token, owner, Ip);
if(r) return Ok(r);
return StatusCode(403, new { message = "403 Denied" });
var r = await comicService.InitAsync(token, owner, Ip);
return r ? Ok("Initialize Success") : _403;
}
[HttpGet]
public async Task<IActionResult> QueryCollections(string token)
{
var r = await rs.Query(ImageFolder, token, Ip);
if(r == null)
return StatusCode(401, new { message = "Unauthorized" });
return Ok(r.NaturalSort(x => x));
var r = await comicService.QueryCollections(token, Ip);
return r != null ? Ok(r.NaturalSort(x => x)) : _403;
}
[HttpGet("{id}")]
public async Task<IActionResult> Query(string id, string token)
{
var d = Helpers.SafePathCombine(ImageFolder, [id, "summary.json"]);
if (d == null) return StatusCode(403, new { message = "403 Denied" });
var r = await rs.Get(d, token, Ip);
if (!r) return StatusCode(403, new { message = "403 Denied" });
return Ok(await System.IO.File.ReadAllTextAsync(d));
var r = await comicService.Query(id, token, Ip);
return r != null ? Ok(r) : _403;
}
[HttpPost("bulkquery")]
public async Task<IActionResult> QueryBulk([FromQuery] string token, [FromBody] string[] id)
{
var db = id.Select(x => Helpers.SafePathCombine(ImageFolder, [x, "summary.json"])).ToArray();
if (db.Any(x => x == null))
return BadRequest();
if(!await rs.GetAll(db!, token, Ip))
return StatusCode(403, new { message = "403 Denied" });
var rc = db.Select(x => System.IO.File.ReadAllTextAsync(x!)).ToArray();
string[] rcs = await Task.WhenAll(rc);
var rjs = rcs.Select(JsonConvert.DeserializeObject<Comic>).Select(x => x!).ToArray();
return Ok(JsonConvert.SerializeObject(rjs));
var r = await comicService.QueryBulk(token, id, Ip);
return Ok(JsonConvert.SerializeObject(r));
}
[HttpPost("{id}/bookmark")]
public async Task<IActionResult> Bookmark(string id, string token, [FromBody] Bookmark bookmark)
{
var d = Helpers.SafePathCombine(ImageFolder, [id, "summary.json"]);
if (d == null) return StatusCode(403, new { message = "403 Denied" });
var r = await rs.Update(d, token, Ip);
if (!r) return StatusCode(403, new { message = "403 Denied" });
Comic c = JsonConvert.DeserializeObject<Comic>(await System.IO.File.ReadAllTextAsync(d))!;
var bookmarkPage = Helpers.SafePathCombine(ImageFolder, [id, bookmark.Page]);
if(!System.IO.File.Exists(bookmarkPage))
return BadRequest();
c.Bookmarks.Add(bookmark);
var o = JsonConvert.SerializeObject(c);
await System.IO.File.WriteAllTextAsync(d, o);
return Ok();
var r = await comicService.Bookmark(id, token, bookmark, Ip);
return r ? Ok("Success") : _403;
}
[HttpGet("{id}/{file}")]
public async Task<IActionResult> Get(string id, string file, string token)
{
var d = Helpers.SafePathCombine(ImageFolder, [id, file]);
if (d == null) return StatusCode(403, new { message = "403 Denied" });
var r = await rs.Get(d, token, Ip);
if (!r) return StatusCode(403, new { message = "403 Denied" });
return PhysicalFile(d, "image/jpeg", enableRangeProcessing: true);
var r = await comicService.Page(id, file, token, Ip);
return r ?? _403;
}
}

View File

@@ -1,4 +1,6 @@
using Abyss.Components.Services;
using Abyss.Components.Services.Media;
using Abyss.Components.Services.Misc;
using Abyss.Components.Static;
using Microsoft.AspNetCore.Mvc;
@@ -14,11 +16,11 @@ public class LiveController(ResourceService rs, ConfigureService config): BaseCo
public async Task<IActionResult> AddLive(string id, string token, int owner)
{
var d = Helpers.SafePathCombine(LiveFolder, [id]);
if (d == null) return StatusCode(403, new { message = "403 Denied" });
if (d == null) return _403;
bool r = await rs.Include(d, token, Ip, owner, "rw,--,--");
return r ? Ok("Success") : BadRequest();
return r ? Ok("Success") : _400;
}
[HttpDelete("{id}")]
@@ -26,31 +28,25 @@ public class LiveController(ResourceService rs, ConfigureService config): BaseCo
{
var d = Helpers.SafePathCombine(LiveFolder, [id]);
if (d == null)
return StatusCode(403, new { message = "403 Denied" });
return _403;
bool r = await rs.Exclude(d, token, Ip);
return r ? Ok("Success") : BadRequest();
return r ? Ok("Success") : _400;
}
[HttpGet("{id}/{token}/{item}")]
public async Task<IActionResult> GetLive(string id, string token, string item)
{
var d = Helpers.SafePathCombine(LiveFolder, [id, item]);
var f = Helpers.SafePathCombine(LiveFolder, [id]);
if (d == null || f == null) return BadRequest();
if (d == null) return _400;
// TODO: (History)ffplay does not add the m3u8 query parameter in ts requests, so special treatment is given to ts here
// TODO: (History)It should be pointed out that this implementation is not secure and should be modified in subsequent updates
// TODO: It's still not very elegant, but it's a bit better to some extent
bool r = await rs.Valid(f, token, OperationType.Read, Ip);
if(!r) return StatusCode(403, new { message = "403 Denied" });
if(System.IO.File.Exists(d))
return PhysicalFile(d, Helpers.GetContentType(d));
else
return NotFound();
var r = await rs.Get(d, token, Ip, Helpers.GetContentType(d));
return r ?? _404;
}
}

View File

@@ -1,218 +1,79 @@
using Abyss.Components.Services;
using Abyss.Components.Services.Media;
using Abyss.Components.Static;
using Abyss.Components.Tools;
using Abyss.Model;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.StaticFiles;
using Newtonsoft.Json;
namespace Abyss.Components.Controllers.Media;
using Task = System.Threading.Tasks.Task;
[ApiController]
[Route("api/[controller]")]
public class VideoController(ILogger<VideoController> logger, ResourceService rs, ConfigureService config)
public class VideoController(VideoService videoService)
: BaseController
{
private ILogger<VideoController> _logger = logger;
public readonly string VideoFolder = Path.Combine(config.MediaRoot, "Videos");
[HttpPost("init")]
public async Task<IActionResult> InitAsync(string token, string owner)
{
var r = await rs.Initialize(VideoFolder, token, owner, Ip);
if (r) return Ok(r);
return StatusCode(403, new { message = "403 Denied" });
if (await videoService.Init(token, owner, Ip))
return Ok("Initialized Successfully");
return _403;
}
[HttpGet]
public async Task<IActionResult> GetClass(string token)
{
var r = (await rs.Query(VideoFolder, token, Ip))?.SortLikeWindows();
if (r == null)
return StatusCode(401, new { message = "Unauthorized" });
return Ok(r);
var r = await videoService.GetClasses(token, Ip);
return r != null ? Ok(r) : _403;
}
[HttpGet("{klass}")]
public async Task<IActionResult> QueryClass(string klass, string token)
{
var d = Helpers.SafePathCombine(VideoFolder, klass);
if (d == null) return StatusCode(403, new { message = "403 Denied" });
var r = await rs.Query(d, token, Ip);
if (r == null) return StatusCode(401, new { message = "Unauthorized" });
return Ok(r);
var r = await videoService.QueryClass(klass, token, Ip);
return r != null ? Ok(r) : _403;
}
[HttpGet("{klass}/{id}")]
public async Task<IActionResult> QueryVideo(string klass, string id, string token)
{
var d = Helpers.SafePathCombine(VideoFolder, [klass, id, "summary.json"]);
if (d == null) return StatusCode(403, new { message = "403 Denied" });
var r = await rs.Get(d, token, Ip);
if (!r) return StatusCode(403, new { message = "403 Denied" });
return Ok(await System.IO.File.ReadAllTextAsync(d));
var r = await videoService.QueryVideo(klass, id, token, Ip);
return r != null ? Ok(r) : _403;
}
[HttpPost("{klass}/bulkquery")]
public async Task<IActionResult> QueryBulk([FromQuery] string token, [FromBody] string[] id,
[FromRoute] string klass)
{
var db = id.Select(x => Helpers.SafePathCombine(VideoFolder, [klass, x, "summary.json"])).ToArray();
if (db.Any(x => x == null))
return BadRequest();
if (!await rs.GetAll(db!, token, Ip))
return StatusCode(403, new { message = "403 Denied" });
var rc = db.Select(x => System.IO.File.ReadAllTextAsync(x!)).ToArray();
string[] rcs = await Task.WhenAll(rc);
var rjs = rcs.Select(JsonConvert.DeserializeObject<Video>).Select(x => x!).ToList();
return Ok(JsonConvert.SerializeObject(rjs));
var r = await videoService.QueryBulk(klass, id, token, Ip);
return Ok(JsonConvert.SerializeObject(r));
}
[HttpGet("{klass}/{id}/cover")]
public async Task<IActionResult> Cover(string klass, string id, string token)
{
var d = Helpers.SafePathCombine(VideoFolder, [klass, id, "cover.jpg"]);
if (d == null) return StatusCode(403, new { message = "403 Denied" });
var r = await rs.Get(d, token, Ip);
if (!r) return StatusCode(403, new { message = "403 Denied" });
return PhysicalFile(d, "image/jpeg", enableRangeProcessing: true);
var r = await videoService.Cover(klass, id, token, Ip);
return r ?? _403;
}
[HttpGet("{klass}/{id}/gallery/{pic}")]
public async Task<IActionResult> Gallery(string klass, string id, string pic, string token)
{
var d = Helpers.SafePathCombine(VideoFolder, [klass, id, "gallery", pic]);
if (d == null) return StatusCode(403, new { message = "403 Denied" });
var r = await rs.Get(d, token, Ip);
if (!r) return StatusCode(403, new { message = "403 Denied" });
return PhysicalFile(d, "image/jpeg", enableRangeProcessing: true);
var r = await videoService.Gallery(klass, id, pic, token, Ip);
return r ?? _403;
}
[HttpGet("{klass}/{id}/subtitle")]
public async Task<IActionResult> Subtitle(string klass, string id, string token)
{
var folder = Helpers.SafePathCombine(VideoFolder, new[] { klass, id });
if (folder == null)
return StatusCode(403, new { message = "403 Denied" });
string? subtitlePath = null;
try
{
var preferredVtt = Path.Combine(folder, "subtitle.vtt");
if (System.IO.File.Exists(preferredVtt))
{
subtitlePath = preferredVtt;
}
else
{
subtitlePath = Directory.EnumerateFiles(folder, "*.vtt").FirstOrDefault();
if (subtitlePath == null)
{
var preferredAss = Path.Combine(folder, "subtitle.ass");
if (System.IO.File.Exists(preferredAss))
{
subtitlePath = preferredAss;
}
else
{
subtitlePath = Directory.EnumerateFiles(folder, "*.ass").FirstOrDefault();
}
}
}
}
catch (DirectoryNotFoundException)
{
return NotFound(new { message = "video folder not found" });
}
if (subtitlePath == null)
return NotFound(new { message = "subtitle not found" });
var r = await rs.Get(subtitlePath, token, Ip);
if (!r)
return StatusCode(403, new { message = "403 Denied" });
var ext = Path.GetExtension(subtitlePath).ToLowerInvariant();
var contentType = ext switch
{
".vtt" => "text/vtt",
".ass" => "text/x-ssa",
_ => "text/plain"
};
return PhysicalFile(subtitlePath, contentType, enableRangeProcessing: false);
var r = await videoService.Subtitle(klass, id, token, Ip);
return r ?? _404;
}
[HttpGet("{klass}/{id}/av")]
public async Task<IActionResult> Av(string klass, string id, string token)
{
var folder = Helpers.SafePathCombine(VideoFolder, new[] { klass, id });
if (folder == null) return StatusCode(403, new { message = "403 Denied" });
var allowedExt = new[] { ".mp4", ".mkv", ".webm", ".mov", ".ogg" };
string? videoPath = null;
foreach (var ext in allowedExt)
{
var p = Path.Combine(folder, "video" + ext);
if (System.IO.File.Exists(p))
{
videoPath = p;
break;
}
}
if (videoPath == null)
{
try
{
videoPath = Directory.EnumerateFiles(folder)
.FirstOrDefault(f => allowedExt.Contains(Path.GetExtension(f).ToLowerInvariant()));
}
catch (DirectoryNotFoundException)
{
return NotFound(new { message = "video folder not found" });
}
}
if (videoPath == null) return NotFound(new { message = "video not found" });
var r = await rs.Get(videoPath, token, Ip);
if (!r) return StatusCode(403, new { message = "403 Denied" });
var provider = new FileExtensionContentTypeProvider();
if (!provider.TryGetContentType(videoPath, out var contentType))
{
var ext = Path.GetExtension(videoPath).ToLowerInvariant();
contentType = ext switch
{
".mkv" => "video/x-matroska",
".mp4" => "video/mp4",
".webm" => "video/webm",
".mov" => "video/quicktime",
".ogg" => "video/ogg",
_ => "application/octet-stream",
};
}
return PhysicalFile(videoPath, contentType, enableRangeProcessing: true);
var r = await videoService.Av(klass, id, token, Ip);
return r ?? _403;
}
}

View File

@@ -1,5 +1,7 @@
using System.Text;
using Abyss.Components.Services;
using Abyss.Components.Services.Media;
using Abyss.Components.Services.Security;
using Abyss.Components.Static;
using Microsoft.AspNetCore.Mvc;
@@ -18,12 +20,12 @@ public class RootController(ILogger<RootController> logger, UserService userServ
if (userService.Validate(token, Ip) != 1)
{
logger.LogInformation("Chmod authorization failed for token: {Token}", token);
return StatusCode(401, "Unauthorized");
return _401;
}
bool r = await resourceService.Chmod(path, token, permission, Ip, recursive == "true");
logger.LogInformation("Chmod operation completed with result: {Result}", r);
return r ? Ok() : StatusCode(502);
return r ? Ok() : StatusCode(500);
}
[HttpPost("chown")]
@@ -34,7 +36,7 @@ public class RootController(ILogger<RootController> logger, UserService userServ
if (userService.Validate(token, Ip) != 1)
{
logger.LogInformation("Chown authorization failed for token: {Token}", token);
return StatusCode(401, "Unauthorized");
return _401;
}
bool r = await resourceService.Chown(path, token, owner, Ip, recursive == "true");
@@ -50,13 +52,13 @@ public class RootController(ILogger<RootController> logger, UserService userServ
if (userService.Validate(token, Ip) != 1)
{
logger.LogInformation("Ls authorization failed for token: {Token}", token);
return StatusCode(401, "Unauthorized");
return _401;
}
if (string.IsNullOrWhiteSpace(path))
{
logger.LogInformation("Ls method received empty path parameter");
return BadRequest("path is required");
return _400;
}
try
@@ -67,10 +69,10 @@ public class RootController(ILogger<RootController> logger, UserService userServ
if (!Directory.Exists(fullPath))
{
logger.LogInformation("Directory does not exist: {FullPath}", fullPath);
return BadRequest("Path does not exist or is not a directory");
return _400;
}
var entries = Directory.EnumerateFileSystemEntries(fullPath, "*", SearchOption.TopDirectoryOnly);
var entries = Directory.EnumerateFileSystemEntries(fullPath, "*", SearchOption.TopDirectoryOnly).ToArray();
logger.LogInformation("Found {Count} entries in directory", entries.Count());
var sb = new StringBuilder();
@@ -115,12 +117,12 @@ public class RootController(ILogger<RootController> logger, UserService userServ
if (userService.Validate(token, Ip) != 1)
{
logger.LogInformation("Init authorization failed for token: {Token}", token);
return StatusCode(401, "Unauthorized");
return _401;
}
var r = await resourceService.Initialize(path, token, owner, Ip);
if (r) return Ok(r);
return StatusCode(403, new { message = "403 Denied" });
return _403;
}
private static string ConvertToLsPerms(string permRaw, bool isDirectory)
@@ -135,7 +137,7 @@ public class RootController(ILogger<RootController> logger, UserService userServ
return (isDirectory ? 'd' : '-') + "---------";
}
string makeTriplet(string token)
string MakeTriplet(string token)
{
if (token.Length < 2) token = "--";
var r = token.Length > 0 && token[0] == 'r' ? 'r' : '-';
@@ -144,9 +146,9 @@ public class RootController(ILogger<RootController> logger, UserService userServ
return $"{r}{w}{x}";
}
var owner = makeTriplet(parts[0]);
var group = makeTriplet(parts[1]);
var other = makeTriplet(parts[2]);
var owner = MakeTriplet(parts[0]);
var group = MakeTriplet(parts[1]);
var other = MakeTriplet(parts[2]);
return (isDirectory ? 'd' : '-') + owner + group + other;
}

View File

@@ -2,8 +2,10 @@
using System.Text.RegularExpressions;
using Abyss.Components.Services;
using Abyss.Components.Services.Security;
using Abyss.Components.Static;
using Abyss.Model;
using Abyss.Model.Security;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.RateLimiting;
@@ -19,7 +21,7 @@ public class UserController(UserService userService, ILogger<UserController> log
{
var c = await userService.Challenge(user);
if (c == null)
return StatusCode(403, new { message = "Access forbidden" });
return _403;
return Ok(c);
}
@@ -29,7 +31,10 @@ public class UserController(UserService userService, ILogger<UserController> log
{
var r = await userService.Verify(user, response.Response, Ip);
if (r == null)
return StatusCode(403, new { message = "Access forbidden" });
return _403;
return Ok(r);
}
@@ -39,7 +44,7 @@ public class UserController(UserService userService, ILogger<UserController> log
var u = userService.Validate(token, Ip);
if (u == -1)
{
return StatusCode(401, new { message = "Invalid" });
return _401;
}
return Ok(u);
@@ -51,7 +56,7 @@ public class UserController(UserService userService, ILogger<UserController> log
var u = userService.Validate(token, Ip);
if (u == -1)
{
return StatusCode(401, new { message = "Invalid" });
return _401;
}
userService.Destroy(token);
@@ -64,21 +69,21 @@ public class UserController(UserService userService, ILogger<UserController> log
// Valid token
var r = await userService.Verify(user, creating.Response, Ip);
if (r == null)
return StatusCode(403, new { message = "Denied" });
return _403;
// User exists ?
var cu = await userService.QueryUser(creating.Name);
if (cu != null)
return StatusCode(403, new { message = "Denied" });
return _403;
// Valid username string
if (!IsAlphanumeric(creating.Name))
return StatusCode(403, new { message = "Denied" });
return _403;
// Valid parent && Privilege
var ou = await userService.QueryUser(userService.Validate(r, Ip));
if (creating.Privilege > ou?.Privilege || ou == null)
return StatusCode(403, new { message = "Denied" });
return _403;
await userService.CreateUser(new User
{
@@ -98,13 +103,13 @@ public class UserController(UserService userService, ILogger<UserController> log
var caller = userService.Validate(token, Ip);
if (caller != 1)
{
return StatusCode(403, new { message = "Access forbidden" });
return _403;
}
var target = await userService.QueryUser(user);
if (target == null)
{
return StatusCode(404, new { message = "User not found" });
return _403;
}
var ipToBind = string.IsNullOrWhiteSpace(bindIp) ? Ip : bindIp;

View File

@@ -1,6 +1,9 @@
using Abyss.Components.Services;
using Abyss.Components.Services.Media;
using Abyss.Components.Services.Misc;
using Abyss.Components.Static;
using Abyss.Model;
using Abyss.Model.Media;
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

View File

@@ -0,0 +1,67 @@
using Abyss.Components.Services.Misc;
using Abyss.Components.Static;
using Abyss.Model.Media;
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json;
using Task = System.Threading.Tasks.Task;
namespace Abyss.Components.Services.Media;
public class ComicService(ILogger<ComicService> logger, ResourceService rs, ConfigureService config)
{
public readonly string ImageFolder = Path.Combine(config.MediaRoot, "Images");
public async Task<bool> InitAsync(string token, string owner, string ip)
=> await rs.Initialize(ImageFolder, token, owner, ip);
public async Task<string[]?> QueryCollections(string token, string ip)
=> await rs.Query(ImageFolder, token, ip);
public async Task<string?> Query(string id, string token, string ip)
{
var d = Helpers.SafePathCombine(ImageFolder, [id, "summary.json"]);
if(d != null)
return await rs.GetString(d, token, ip);
return null;
}
public async Task<Comic?[]> QueryBulk(string token, string[] id, string ip)
{
var db = id.Select(x => Helpers.SafePathCombine(ImageFolder, [x, "summary.json"])).ToArray();
if (db.Any(x => x == null))
return [];
var sm = await rs.GetAllString(db!, token, ip);
return sm.Select(x => x.Value == null ? null : JsonConvert.DeserializeObject<Comic>(x.Value)).ToArray();
}
public async Task<bool> Bookmark(string id, string token, Bookmark bookmark, string ip)
{
var d = Helpers.SafePathCombine(ImageFolder, [id, "summary.json"]);
if (d == null)
return false;
Comic c = JsonConvert.DeserializeObject<Comic>(await File.ReadAllTextAsync(d))!;
var bookmarkPage = Helpers.SafePathCombine(ImageFolder, [id, bookmark.Page]);
if (File.Exists(bookmarkPage))
{
c.Bookmarks.Add(bookmark);
var o = JsonConvert.SerializeObject(c);
return await rs.UpdateString(d, token, ip, o);
}
return false;
}
public async Task<PhysicalFileResult?> Page(string id, string file, string token, string ip)
{
var d = Helpers.SafePathCombine(ImageFolder, [id, file]);
if (d != null)
{
return await rs.Get(d, token, ip, "image/jpeg");
}
return null;
}
}

View File

@@ -1,6 +1,7 @@
using Abyss.Components.Services.Misc;
using SQLite;
using Index = Abyss.Model.Index;
namespace Abyss.Components.Services;
using Index = Abyss.Model.Media.Index;
namespace Abyss.Components.Services.Media;
public class IndexService: IAsyncDisposable
{

View File

@@ -1,11 +1,12 @@
using System.IO.Hashing;
using System.Text;
using System.Text.RegularExpressions;
using Abyss.Components.Services.Misc;
using Abyss.Components.Static;
using Abyss.Model;
using Abyss.Model.Media;
using SQLite;
namespace Abyss.Components.Services;
namespace Abyss.Components.Services.Media;
public class ResourceDatabaseService
{

View File

@@ -1,9 +1,13 @@
// ResourceService.cs
using Abyss.Components.Services.Misc;
using Abyss.Components.Services.Security;
using Abyss.Components.Static;
using Abyss.Model;
using Abyss.Model.Media;
using Abyss.Model.Security;
using Microsoft.AspNetCore.Mvc;
namespace Abyss.Components.Services;
namespace Abyss.Components.Services.Media;
public enum OperationType
{
@@ -28,7 +32,7 @@ public class ResourceService
}
// Create UID only for resources, without considering advanced hash security such as adding salt
public async Task<Dictionary<string, bool>> ValidAny(string[] paths, string token, OperationType type, string ip)
private async Task<Dictionary<string, bool>> ValidAny(string[] paths, string token, OperationType type, string ip)
{
var result = new Dictionary<string, bool>(StringComparer.OrdinalIgnoreCase);
@@ -216,7 +220,7 @@ public class ResourceService
return result;
}
private async Task<bool> ValidAll(string[] paths, string token, OperationType type, string ip)
private async Task<bool> ValidAll(string[] paths, string token, OperationType type, string ip)
{
if (paths.Length == 0)
{
@@ -340,7 +344,7 @@ public class ResourceService
return true;
}
public async Task<bool> Valid(string path, string token, OperationType type, string ip)
private async Task<bool> Valid(string path, string token, OperationType type, string ip)
{
// Path is abs path here, due to Helpers.SafePathCombine
if (!path.StartsWith(Path.GetFullPath(_config.MediaRoot), StringComparison.OrdinalIgnoreCase))
@@ -485,19 +489,52 @@ public class ResourceService
}
}
public async Task<bool> Get(string path, string token, string ip)
public async Task<PhysicalFileResult?> Get(string path, string token, string ip, string contentType)
{
return await Valid(path, token, OperationType.Read, ip);
var b = await Valid(path, token, OperationType.Read, ip);
if (b) return new PhysicalFileResult(path, contentType)
{
EnableRangeProcessing = true
};
return null;
}
public async Task<bool> GetAll(string[] path, string token, string ip)
public async Task<string?> GetString(string path, string token, string ip)
{
return await ValidAll(path, token, OperationType.Read, ip);
var b = await Valid(path, token, OperationType.Read, ip);
if (b)
{
return await File.ReadAllTextAsync(path);
}
return null;
}
public async Task<bool> Update(string path, string token, string ip)
public async Task<Dictionary<string, string?>> GetAllString(string[] paths, string token, string ip)
{
return await Valid(path, token, OperationType.Write, ip);
Dictionary<string, string?> result = new();
var validMap = await ValidAny(paths, token, OperationType.Read, ip);
foreach (var entry in validMap)
{
if (entry.Value)
{
result[entry.Key] = await File.ReadAllTextAsync(entry.Key);
}
}
return result;
}
public async Task<bool> UpdateString(string path, string token, string ip, string content)
{
var b = await Valid(path, token, OperationType.Write, ip);
if (b)
{
await File.WriteAllTextAsync(path, content);
return true;
}
return false;
}
public async Task<bool> Initialize(string path, string token, string owner, string ip)

View File

@@ -1,10 +1,12 @@
using Abyss.Components.Services.Misc;
using Abyss.Components.Services.Security;
using Abyss.Components.Static;
using Abyss.Model;
using Abyss.Model.Media;
using Newtonsoft.Json;
using SQLite;
using Task = Abyss.Model.Task;
using Task = Abyss.Model.Media.Task;
namespace Abyss.Components.Services;
namespace Abyss.Components.Services.Media;
@@ -24,7 +26,7 @@ public class TaskService(ILogger<TaskService> logger, ConfigureService config, R
foreach (var i in r ?? [])
{
var p = Helpers.SafePathCombine(TaskFolder, [i, "task.json"]);
var c = JsonConvert.DeserializeObject<Model.Task>(await System.IO.File.ReadAllTextAsync(p ?? ""));
var c = JsonConvert.DeserializeObject<Task>(await System.IO.File.ReadAllTextAsync(p ?? ""));
if(c?.Owner == u) s.Add(i);
}
@@ -58,8 +60,6 @@ public class TaskService(ILogger<TaskService> logger, ConfigureService config, R
private async Task<TaskCreationResponse?> CreateVideoTask(string token, string ip, TaskCreation creation)
{
if(!await rs.Valid(VideoFolder, token, OperationType.Write, ip))
return null;
var u = user.Validate(token, ip);
if(u == -1)
return null;

View File

@@ -0,0 +1,162 @@
using Abyss.Components.Services.Misc;
using Abyss.Components.Static;
using Abyss.Model.Media;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.StaticFiles;
using Newtonsoft.Json;
namespace Abyss.Components.Services.Media;
public class VideoService(ILogger<VideoService> logger, ResourceService rs, ConfigureService config)
{
public readonly string VideoFolder = Path.Combine(config.MediaRoot, "Videos");
public async Task<bool> Init(string token, string owner, string ip) => await rs.Initialize(VideoFolder, token, owner, ip);
public async Task<string[]?> GetClasses(string token, string ip)
=> (await rs.Query(VideoFolder, token, ip))?.SortLikeWindows();
public async Task<string[]?> QueryClass(string klass, string token, string ip)
{
var d = Helpers.SafePathCombine(VideoFolder, klass);
if (d != null)
{
return await rs.Query(d, token, ip);
}
return null;
}
public async Task<string?> QueryVideo(string klass, string id, string token, string ip)
{
var d = Helpers.SafePathCombine(VideoFolder, [klass, id, "summary.json"]);
if(d != null)
return await rs.GetString(d, token, ip);
return null;
}
public async Task<Video?[]> QueryBulk(string klass, string[] id, string token, string ip)
{
var db = id.Select(x => Helpers.SafePathCombine(VideoFolder, [klass, x, "summary.json"])).ToArray();
if (db.Any(x => x == null))
return [];
var sm = await rs.GetAllString(db!, token, ip);
return sm.Select(x => x.Value == null ? null : JsonConvert.DeserializeObject<Video>(x.Value)).ToArray();
}
public async Task<PhysicalFileResult?> Cover(string klass, string id, string token, string ip)
{
var d = Helpers.SafePathCombine(VideoFolder, [klass, id, "cover.jpg"]);
if (d != null)
{
return await rs.Get(d, token, ip, "image/jpeg");
}
return null;
}
public async Task<PhysicalFileResult?> Gallery(string klass, string id, string pic, string token, string ip)
{
var d = Helpers.SafePathCombine(VideoFolder, [klass, id, "gallery", pic]);
if (d != null)
{
return await rs.Get(d, token, ip, "image/jpeg");
}
return null;
}
public async Task<PhysicalFileResult?> Subtitle(string klass, string id, string token, string ip)
{
var folder = Helpers.SafePathCombine(VideoFolder, new[] { klass, id });
if (folder == null)
return null;
string? subtitlePath;
try
{
var preferredVtt = Path.Combine(folder, "subtitle.vtt");
if (File.Exists(preferredVtt))
{
subtitlePath = preferredVtt;
}
else
{
subtitlePath = Directory.EnumerateFiles(folder, "*.vtt").FirstOrDefault();
if (subtitlePath == null)
{
var preferredAss = Path.Combine(folder, "subtitle.ass");
if (File.Exists(preferredAss))
{
subtitlePath = preferredAss;
}
else
{
subtitlePath = Directory.EnumerateFiles(folder, "*.ass").FirstOrDefault();
}
}
}
}
catch (DirectoryNotFoundException)
{
return null;
}
if (subtitlePath == null)
return null;
var ext = Path.GetExtension(subtitlePath).ToLowerInvariant();
var contentType = ext switch
{
".vtt" => "text/vtt",
".ass" => "text/x-ssa",
_ => "text/plain"
};
return await rs.Get(subtitlePath, token, ip, contentType);
}
public async Task<PhysicalFileResult?> Av(string klass, string id, string token, string ip)
{
var folder = Helpers.SafePathCombine(VideoFolder, new[] { klass, id });
if (folder == null)
return null;
var allowedExt = new[] { ".mp4", ".mkv", ".webm", ".mov", ".ogg" };
string? videoPath = null;
foreach (var ext in allowedExt)
{
var p = Path.Combine(folder, "video" + ext);
if (File.Exists(p))
{
videoPath = p;
break;
}
}
if (videoPath == null)
return null;
var provider = new FileExtensionContentTypeProvider();
if (!provider.TryGetContentType(videoPath, out var contentType))
{
var ext = Path.GetExtension(videoPath).ToLowerInvariant();
contentType = ext switch
{
".mkv" => "video/x-matroska",
".mp4" => "video/mp4",
".webm" => "video/webm",
".mov" => "video/quicktime",
".ogg" => "video/ogg",
_ => "application/octet-stream",
};
}
return await rs.Get(videoPath, token, ip, contentType);
}
}

View File

@@ -1,4 +1,4 @@
namespace Abyss.Components.Services;
namespace Abyss.Components.Services.Misc;
public class ConfigureService
{

View File

@@ -1,9 +1,10 @@
using System.Net;
using System.Net.Sockets;
using System.Text;
using Abyss.Components.Services.Misc;
using Abyss.Components.Tools;
namespace Abyss.Components.Services;
namespace Abyss.Components.Services.Security;
public class AbyssService(ILogger<AbyssService> logger, ConfigureService config, UserService user) : IHostedService, IDisposable
{

View File

@@ -3,13 +3,14 @@
using System.Security.Cryptography;
using System.Text;
using Abyss.Model;
using Abyss.Components.Services.Misc;
using Abyss.Model.Security;
using Microsoft.Extensions.Caching.Memory;
using NSec.Cryptography;
using SQLite;
using Task = System.Threading.Tasks.Task;
namespace Abyss.Components.Services;
namespace Abyss.Components.Services.Security;
public class UserService
{

View File

@@ -5,6 +5,11 @@ namespace Abyss.Components.Static;
public abstract class BaseController : Controller
{
protected IActionResult _403 => StatusCode(403, new { message = "Access Denied" });
protected IActionResult _400 => StatusCode(400, new { message = "Bad Request" });
protected IActionResult _401 => StatusCode(404, new { message = "Unauthorized" });
protected IActionResult _404 => StatusCode(404, new { message = "Not Found" });
private string? _ip;
protected string Ip

View File

@@ -11,6 +11,7 @@ using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Text;
using Abyss.Components.Services;
using Abyss.Components.Services.Security;
using Microsoft.AspNetCore.Authentication;
using NSec.Cryptography;

View File

@@ -112,7 +112,7 @@ public static Dictionary<string, List<T>> Cluster<T>(
public bool EndsWithNumber { get; }
public object Payload { get; }
public Item(string original, object payload = null)
public Item(string original, object? payload = null)
{
Original = original;
Payload = payload ?? original;

View File

@@ -1,6 +1,6 @@
using Newtonsoft.Json;
namespace Abyss.Model;
namespace Abyss.Model.Media;
public class Bookmark
{

View File

@@ -1,4 +1,4 @@
namespace Abyss.Model;
namespace Abyss.Model.Media;
public enum ChipState
{

View File

@@ -1,6 +1,6 @@
using Newtonsoft.Json;
namespace Abyss.Model;
namespace Abyss.Model.Media;
public class Comic
{

View File

@@ -1,4 +1,4 @@
namespace Abyss.Model;
namespace Abyss.Model.Media;
public class Comment
{

View File

@@ -1,6 +1,6 @@
using SQLite;
namespace Abyss.Model;
namespace Abyss.Model.Media;
[Table("Index")]
public class Index

View File

@@ -1,6 +1,6 @@
using SQLite;
namespace Abyss.Model;
namespace Abyss.Model.Media;
[Table("ResourceAttributes")]
public class ResourceAttribute

View File

@@ -1,4 +1,4 @@
namespace Abyss.Model;
namespace Abyss.Model.Media;
public enum TaskType
{

View File

@@ -1,4 +1,4 @@
namespace Abyss.Model;
namespace Abyss.Model.Media;
public class TaskCreation
{

View File

@@ -0,0 +1,27 @@
using Newtonsoft.Json;
namespace Abyss.Model.Media;
public class Video
{
[JsonProperty("name")] public string Name { get; set; } = "";
[JsonProperty("duration")]
public ulong Duration { get; set; }
[JsonProperty("gallery")]
public List<string> Gallery { get; set; } = new();
[JsonProperty("comment")]
public List<Comment> Comment { get; set; } = new();
[JsonProperty("star")]
public bool Star { get; set; }
[JsonProperty("like")] public uint Like { get; set; }
[JsonProperty("author")] public string Author { get; set; } = "";
[JsonProperty("group")]
public string? Group { get; set; }
}

View File

@@ -1,4 +1,4 @@
namespace Abyss.Model;
namespace Abyss.Model.Security;
public class ChallengeResponse
{

View File

@@ -1,6 +1,6 @@
using SQLite;
namespace Abyss.Model;
namespace Abyss.Model.Security;
[Table("Users")]
public class User

View File

@@ -1,4 +1,4 @@
namespace Abyss.Model;
namespace Abyss.Model.Security;
public class UserCreating
{

View File

@@ -1,13 +0,0 @@
namespace Abyss.Model;
public class Video
{
public string name;
public ulong duration;
public List<string> gallery = new();
public List<Comment> comment = new();
public bool star;
public uint like;
public string author;
public string? group;
}

View File

@@ -1,12 +1,13 @@
using System.Text.RegularExpressions;
using System.Threading.RateLimiting;
using Abyss.Components.Controllers.Middleware;
using Abyss.Components.Controllers.Task;
using Abyss.Components.Services;
using Abyss.Misc;
using Abyss.Model;
using Abyss.Components.Services.Media;
using Abyss.Components.Services.Misc;
using Abyss.Components.Services.Security;
using Microsoft.AspNetCore.RateLimiting;
using Newtonsoft.Json;
namespace Abyss;
@@ -26,6 +27,8 @@ public class Program
builder.Services.AddSingleton<TaskController>();
builder.Services.AddSingleton<TaskService>();
builder.Services.AddSingleton<IndexService>();
builder.Services.AddSingleton<VideoService>();
builder.Services.AddSingleton<ComicService>();
builder.Services.AddHostedService<AbyssService>();
builder.Services.AddRateLimiter(options =>