[feat] Sort query results

This commit is contained in:
acite
2025-08-27 15:16:50 +08:00
parent 69509e4b87
commit 64aa7a2fdd
4 changed files with 191 additions and 30 deletions

View File

@@ -10,10 +10,10 @@
</component>
<component name="ChangeListManager">
<list default="true" id="bf317275-3039-49bb-a475-725a800a0cce" name="Changes" comment="">
<change beforePath="$PROJECT_DIR$/.gitignore" beforeDir="false" afterPath="$PROJECT_DIR$/.gitignore" 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/Components/Controllers/Media/VideoController.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Abyss/Components/Controllers/Media/VideoController.cs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Abyss/appsettings.json" beforeDir="false" afterPath="$PROJECT_DIR$/Abyss/appsettings.json" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Abyss/Components/Services/UserService.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Abyss/Components/Services/UserService.cs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Abyss/Components/Static/Helpers.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Abyss/Components/Static/Helpers.cs" afterDir="false" />
</list>
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
@@ -32,10 +32,14 @@
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/DecompilerCache/decompiler/7598e47d5cdf4107ba88f8220720fdc89000/a6/79d67871/xxHash128.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/DecompilerCache/decompiler/f09ccaeb94c34c2299acd3efee0facee1a400/81/137b58b4/Key.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Abyss/Components/Controllers/AbyssController.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="mock:///home/acite/embd/WebProjects/Abyss/Abyss/Components/Controllers/Media/VideoController.cs" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///home/acite/embd/WebProjects/Abyss/Abyss/Components/Controllers/Media/VideoController.cs" root0="SKIP_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Abyss/Components/Controllers/Security/UserController.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/ResourceService.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Abyss/Components/Services/UserService.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="mock:///home/acite/embd/WebProjects/Abyss/Abyss/Components/Services/UserService.cs" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///home/acite/embd/WebProjects/Abyss/Abyss/Components/Services/UserService.cs" root0="SKIP_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Abyss/Components/Static/Helpers.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Abyss/Model/ChallengeResponse.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Abyss/Model/ResourceAttribute.cs" root0="FORCE_HIGHLIGHTING" />
@@ -52,32 +56,33 @@
<option name="hideEmptyMiddlePackages" value="true" />
<option name="showLibraryContents" value="true" />
</component>
<component name="PropertiesComponent"><![CDATA[{
"keyToString": {
".NET Launch Settings Profile.Abyss: http.executor": "Run",
".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.30266345",
"git-widget-placeholder": "main",
"last_opened_file_path": "/home/acite/embd/WebProjects/Abyss/.gitignore",
"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": "preferences.pluginManager",
"vue.rearranger.settings.migration": "true"
<component name="PropertiesComponent">{
&quot;keyToString&quot;: {
&quot;.NET Launch Settings Profile.Abyss: http.executor&quot;: &quot;Run&quot;,
&quot;.NET Launch Settings Profile.Abyss: https.executor&quot;: &quot;Run&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.30266345&quot;,
&quot;git-widget-placeholder&quot;: &quot;main&quot;,
&quot;last_opened_file_path&quot;: &quot;/opt/security/https/server&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;preferences.pluginManager&quot;,
&quot;vue.rearranger.settings.migration&quot;: &quot;true&quot;
}
}]]></component>
<component name="RunManager" selected=".NET Launch Settings Profile.Abyss: http">
}</component>
<component name="RunManager" selected="Publish to folder.Publish Abyss to folder x86">
<configuration name="Publish Abyss to folder x86" type="DotNetFolderPublish" factoryName="Publish to folder">
<riderPublish configuration="Release" platform="Any CPU" produce_single_file="true" self_contained="true" target_folder="$PROJECT_DIR$/Abyss/bin/Release/net9.0/linux-x64/publish" target_framework="net9.0" uuid_high="3690631506471504162" uuid_low="-4858628519588143325">
<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">
<runtimes>
<item value="linux-x64" />
</runtimes>
@@ -85,7 +90,7 @@
<method v="2" />
</configuration>
<configuration name="Publish Abyss to folder" type="DotNetFolderPublish" factoryName="Publish to folder">
<riderPublish configuration="Release" platform="Any CPU" self_contained="true" target_folder="$PROJECT_DIR$/Abyss/bin/Release/net9.0/publish" target_framework="net9.0" uuid_high="3690631506471504162" uuid_low="-4858628519588143325">
<riderPublish configuration="Release" platform="Any CPU" produce_single_file="true" self_contained="true" target_folder="$PROJECT_DIR$/Abyss/bin/Release/net9.0/publish" target_framework="net9.0" uuid_high="3690631506471504162" uuid_low="-4858628519588143325">
<runtimes>
<item value="linux-arm64" />
</runtimes>
@@ -147,6 +152,13 @@
<option name="Build" />
</method>
</configuration>
<list>
<item itemvalue=".NET Launch Settings Profile.Abyss: http" />
<item itemvalue=".NET Launch Settings Profile.Abyss: https" />
<item itemvalue=".NET Project.AbyssCli" />
<item itemvalue="Publish to folder.Publish Abyss to folder" />
<item itemvalue="Publish to folder.Publish Abyss to folder x86" />
</list>
</component>
<component name="TaskManager">
<task active="true" id="Default" summary="Default task">
@@ -159,7 +171,10 @@
<workItem from="1755878548611" duration="22675000" />
<workItem from="1755924449835" duration="39604000" />
<workItem from="1756025651909" duration="43000" />
<workItem from="1756121403390" duration="4269000" />
<workItem from="1756121403390" duration="7316000" />
<workItem from="1756145197559" duration="780000" />
<workItem from="1756205686118" duration="5398000" />
<workItem from="1756277940361" duration="862000" />
</task>
<servers />
</component>
@@ -168,6 +183,29 @@
</component>
<component name="UnityCheckinConfiguration" checkUnsavedScenes="true" />
<component name="UnityProjectConfiguration" hasMinimizedUI="false" />
<component name="Vcs.Log.Tabs.Properties">
<option name="TAB_STATES">
<map>
<entry key="MAIN">
<value>
<State>
<option name="FILTERS">
<map>
<entry key="branch">
<value>
<list>
<option value="main" />
</list>
</value>
</entry>
</map>
</option>
</State>
</value>
</entry>
</map>
</option>
</component>
<component name="VcsManagerConfiguration">
<option name="CLEAR_INITIAL_COMMIT_MESSAGE" value="true" />
</component>

View File

@@ -24,7 +24,7 @@ public class VideoController(ILogger<VideoController> logger, ResourceService rs
[HttpGet]
public async Task<IActionResult> GetClass(string token)
{
var r = await rs.Query(VideoFolder, token, Ip);
var r = (await rs.Query(VideoFolder, token, Ip))?.SortLikeWindows();
if(r == null)
return StatusCode(401, new { message = "Unauthorized" });
@@ -38,6 +38,7 @@ public class VideoController(ILogger<VideoController> logger, ResourceService rs
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);
@@ -64,6 +65,8 @@ public class VideoController(ILogger<VideoController> logger, ResourceService rs
var r = await rs.Get(d, token, Ip);
if (!r) return StatusCode(403, new { message = "403 Denied" });
_logger.LogInformation($"Cover found for {id}");
return PhysicalFile(d, "image/jpeg", enableRangeProcessing: true);
}

View File

@@ -117,7 +117,7 @@ public class UserService
Destroy(token);
return null;
}
_logger.LogInformation($"Validated {userAndIp}");
// _logger.LogInformation($"Validated {userAndIp}");
return userAndIp?.Split('@')[0];
}
_logger.LogWarning($"Validation failed {token}");

View File

@@ -1,4 +1,7 @@
using System.Globalization;
using System.Text.RegularExpressions;
namespace Abyss.Components.Static;
public static class Helpers
@@ -58,3 +61,120 @@ public enum PathType
NotFound,
AccessDenied
}
public static class StringArrayExtensions
{
public static string[] SortLikeWindows(this string[] array)
{
if (array == null) return null;
if (array.Length == 0) return array;
Array.Sort(array, new WindowsFileNameComparer());
return array;
}
public static string[] SortLikeWindowsDescending(this string[] array)
{
if (array == null) return null;
if (array.Length == 0) return array;
Array.Sort(array, new WindowsFileNameComparerDescending());
return array;
}
public static void SortLikeWindowsInPlace(this string[] array)
{
if (array == null || array.Length == 0) return;
Array.Sort(array, new WindowsFileNameComparer());
}
public static void SortLikeWindowsDescendingInPlace(this string[] array)
{
if (array == null || array.Length == 0) return;
Array.Sort(array, new WindowsFileNameComparerDescending());
}
}
public class WindowsFileNameComparer : IComparer<string>
{
private static readonly Regex _regex = new Regex(@"(\d+|\D+)", RegexOptions.Compiled);
private static readonly CompareInfo _compareInfo = CultureInfo.InvariantCulture.CompareInfo;
public int Compare(string? x, string? y)
{
if (x == null && y == null) return 0;
if (x == null) return -1;
if (y == null) return 1;
if (ReferenceEquals(x, y)) return 0;
var partsX = _regex.Matches(x);
var partsY = _regex.Matches(y);
int minLength = Math.Min(partsX.Count, partsY.Count);
for (int i = 0; i < minLength; i++)
{
string partX = partsX[i].Value;
string partY = partsY[i].Value;
if (long.TryParse(partX, out long numX) && long.TryParse(partY, out long numY))
{
int comparison = numX.CompareTo(numY);
if (comparison != 0) return comparison;
}
else
{
int comparison;
if (ContainsChinese(partX) || ContainsChinese(partY))
{
comparison = _compareInfo.Compare(partX, partY, CompareOptions.IgnoreCase | CompareOptions.IgnoreNonSpace);
}
else
{
comparison = string.Compare(partX, partY, StringComparison.OrdinalIgnoreCase);
}
if (comparison != 0) return comparison;
}
}
return partsX.Count.CompareTo(partsY.Count);
}
private static bool ContainsChinese(string text)
{
if (string.IsNullOrEmpty(text)) return false;
foreach (char c in text)
{
if (c >= 0x4E00 && c <= 0x9FFF)
return true;
if (c >= 0x3400 && c <= 0x4DBF)
return true;
}
return false;
}
}
public class WindowsFileNameComparerDescending : IComparer<string>
{
private static readonly WindowsFileNameComparer _ascendingComparer = new WindowsFileNameComparer();
public int Compare(string x, string y)
{
return _ascendingComparer.Compare(y, x);
}
}
public static class StringNaturalCompare
{
private static readonly WindowsFileNameComparer _comparer = new WindowsFileNameComparer();
public static int Compare(string x, string y)
{
return _comparer.Compare(x, y);
}
}