@*
This file is part of Utatane.
Utatane is free software: you can redistribute it and/or modify it under
the terms of the GNU Affero General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
Utatane is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
more details.
You should have received a copy of the GNU Affero General Public License
along with Utatane. If not, see .
*@
@page "/api/files"
@using System.Diagnostics.CodeAnalysis
@layout EmptyLayout
@( Path == "/" ? "/" : Path.Split('/').Last() )
@foreach (var row in _rows) {
@if (row.IsFile && !row.IsBad) {
⭳
}
@row.Icon
@if (row.IsBad) {
@(row.Name + row.Trail)
} else {
@if (row.IsFile) {
@row.Name
} else {
@row.Name/
}
}
@if (row.IsFile) {
@if (row.IsBad) {
@Utils.FormatFileSize(row.Size)
} else {
@Utils.FormatFileSize(row.Size)
;
}
}
@if (row.IsBad) {
@row.TimeFmt
} else {
@row.TimeFmt
}
}
@* *@
@code {
[SupplyParameterFromQuery(Name = "path")]
public required String? Path { get; set; }
[SupplyParameterFromQuery(Name = "sort-by")]
public required String? SortByColumn { get; set; }
[SupplyParameterFromQuery(Name = "sort-asc")]
public required String SortAsc { get; set; }
private DirectoryInfo _realPath;
private SortByColumns _sortByColumn;
private SortByDirection _sortByDirection;
private IEnumerable _rows;
protected override Task OnInitializedAsync() {
if (String.IsNullOrEmpty(Path))
Path = "/";
else if (Path.EndsWith("/") && Path != "/")
Path = Path.TrimEnd('/');
// prechecked in middleware!
_realPath = Utils.VerifyPath(Path).Value.AsT1();
_sortByColumn = SortByColumn switch {
"name" => SortByColumns.Name,
"size" => SortByColumns.Size,
"time" => SortByColumns.Time,
_ => SortByColumns.Name
};
_sortByDirection = SortAsc switch {
"on" => SortByDirection.Ascending,
_ => SortByDirection.Descending
};
_rows = _realPath.EnumerateFileSystemInfos()
.Select(fsi => new FileRow(fsi, Path ?? "/"))
.OrderByDescending(x => !x.IsFile)
.ThenBy(x => x.Name);
// Console.WriteLine($"Path <{Path}> | Col <{SortByColumn}> | Dir <{SortAsc}>");
// Console.WriteLine($"RealPath <{_realPath}> | Col <{_sortByColumn}> | Dir <{_sortByDirection}>");
return base.OnInitializedAsync();
}
public enum SortByColumns {
Name,
Size,
Time,
}
public enum SortByDirection {
Ascending,
Descending,
}
public class FileRow {
public required FileSystemInfo Fsi { get; init; }
public required bool IsFile { get; init; }
public required bool IsBad { get; init; }
public required String TypeString { get; init; }
public required MarkupString Icon { get; init; }
public required String Name { get; init; }
public required bool IsDotFile { get; init; }
public required long Size { get; init; }
public required String TimeFmt { get; init; }
public required String TimeUnix { get; init; }
public required String? Href { get; init; }
public required char? Trail { get; init; }
[SetsRequiredMembers]
public FileRow(FileSystemInfo baseFsi, String currentPath) {
FileSystemInfo? _fsi;
bool _isBad = false;
try {
_fsi = baseFsi.UnravelLink();
} catch {
_isBad = true;
_fsi = baseFsi;
}
Fsi = _fsi;
IsFile = Fsi is FileInfo;
IsBad = _isBad || !Fsi.IsReadable();
TypeString = IsFile ? "file" : "directory";
Icon = IsBad
? Utils.AbbrIcon($"Server cannot read this {TypeString}", "⚠️")
: Utils.GetIconForFileType(Fsi);
Name = baseFsi.Name; // specifically want whatever its called in the directory we're looking at
IsDotFile = Name.StartsWith('.');
Size = IsBad || !IsFile ? -1 : ((FileInfo)Fsi).Length;
TimeFmt = baseFsi.LastWriteTime.ToUniversalTime().ToString("yyyy-MM-dd HH:mm");
TimeUnix = $"{baseFsi.LastWriteTime.ToUniversalTime().Subtract(DateTime.UnixEpoch).TotalSeconds:N0}";
Href = IsBad
? null
: String.Join('/',
System.IO.Path.Join(currentPath, Name)
.Split('/')
.Select(Uri.EscapeDataString)
);
Trail = IsFile ? null : '/';
}
}
}