Change server HTTP options to allow multiple specified paths.
This is useful if you only want to share specific paths via HTTP, or have the paths stored in multiple places and want to unite them under one URL structure.save-path-regex
parent
d6eff92835
commit
4fb11c1dd7
|
@ -20,14 +20,44 @@ namespace LANCommander.Controllers
|
|||
if (server == null)
|
||||
return NotFound();
|
||||
|
||||
path = path.Replace('/', Path.DirectorySeparatorChar);
|
||||
|
||||
var filename = Path.Combine(server.HTTPRootPath, path);
|
||||
|
||||
if (!System.IO.File.Exists(filename))
|
||||
if (server.HttpPaths == null || server.HttpPaths.Count == 0)
|
||||
return NotFound();
|
||||
|
||||
return File(new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read), "application/octet-stream", Path.GetFileName(filename));
|
||||
// Sanitize
|
||||
if (path == null)
|
||||
path = "/";
|
||||
|
||||
path = path.Trim('/');
|
||||
path = path + "/";
|
||||
|
||||
var httpPath = server.HttpPaths.FirstOrDefault(hp => path.StartsWith(hp.Path.TrimStart('/')));
|
||||
|
||||
// Check to see if there's a root path defined if nothing else matches
|
||||
if (httpPath == null)
|
||||
httpPath = server.HttpPaths.FirstOrDefault(hp => hp.Path == "/");
|
||||
|
||||
if (httpPath == null)
|
||||
return Forbid();
|
||||
|
||||
var relativePath = path.Substring(httpPath.Path.TrimStart('/').Length).Replace('/', Path.DirectorySeparatorChar).TrimStart('\\');
|
||||
|
||||
var localPath = Path.Combine(httpPath.LocalPath, relativePath).TrimEnd('\\');
|
||||
var attrs = System.IO.File.GetAttributes(localPath);
|
||||
|
||||
if ((attrs & FileAttributes.Directory) == FileAttributes.Directory)
|
||||
{
|
||||
if (!System.IO.Directory.Exists(localPath))
|
||||
return NotFound();
|
||||
|
||||
return Json(Directory.GetFileSystemEntries(localPath).Select(fse => fse.Substring(localPath.Length)));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!System.IO.File.Exists(localPath))
|
||||
return NotFound();
|
||||
|
||||
return File(new FileStream(localPath, FileMode.Open, FileAccess.Read, FileShare.Read), "application/octet-stream", Path.GetFileName(localPath));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -119,6 +119,12 @@ namespace LANCommander.Data
|
|||
.IsRequired(true)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
builder.Entity<Server>()
|
||||
.HasMany<ServerHttpPath>()
|
||||
.WithOne(s => s.Server)
|
||||
.IsRequired(true)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
builder.Entity<Redistributable>()
|
||||
.HasMany(r => r.Archives)
|
||||
.WithOne(a => a.Redistributable)
|
||||
|
|
|
@ -27,5 +27,6 @@ namespace LANCommander.Data.Models
|
|||
public virtual Game? Game { get; set; }
|
||||
|
||||
public virtual ICollection<ServerConsole>? ServerConsoles { get; set; }
|
||||
public virtual ICollection<ServerHttpPath>? HttpPaths { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace LANCommander.Data.Models
|
||||
{
|
||||
public class ServerHttpPath : BaseModel
|
||||
{
|
||||
public string LocalPath { get; set; }
|
||||
public string Path { get; set; }
|
||||
|
||||
public Guid ServerId { get; set; }
|
||||
[JsonIgnore]
|
||||
[ForeignKey(nameof(ServerId))]
|
||||
[InverseProperty("HttpPaths")]
|
||||
public virtual Server Server { get; set; }
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,82 @@
|
|||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace LANCommander.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AddServerHttpPaths : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "ServerHttpPath",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<Guid>(type: "TEXT", nullable: false),
|
||||
LocalPath = table.Column<string>(type: "TEXT", nullable: false),
|
||||
Path = table.Column<string>(type: "TEXT", nullable: false),
|
||||
ServerId = table.Column<Guid>(type: "TEXT", nullable: false),
|
||||
ServerId1 = table.Column<Guid>(type: "TEXT", nullable: true),
|
||||
CreatedOn = table.Column<DateTime>(type: "TEXT", nullable: false),
|
||||
CreatedById = table.Column<Guid>(type: "TEXT", nullable: true),
|
||||
UpdatedOn = table.Column<DateTime>(type: "TEXT", nullable: false),
|
||||
UpdatedById = table.Column<Guid>(type: "TEXT", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_ServerHttpPath", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_ServerHttpPath_AspNetUsers_CreatedById",
|
||||
column: x => x.CreatedById,
|
||||
principalTable: "AspNetUsers",
|
||||
principalColumn: "Id");
|
||||
table.ForeignKey(
|
||||
name: "FK_ServerHttpPath_AspNetUsers_UpdatedById",
|
||||
column: x => x.UpdatedById,
|
||||
principalTable: "AspNetUsers",
|
||||
principalColumn: "Id");
|
||||
table.ForeignKey(
|
||||
name: "FK_ServerHttpPath_Servers_ServerId",
|
||||
column: x => x.ServerId,
|
||||
principalTable: "Servers",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "FK_ServerHttpPath_Servers_ServerId1",
|
||||
column: x => x.ServerId1,
|
||||
principalTable: "Servers",
|
||||
principalColumn: "Id");
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_ServerHttpPath_CreatedById",
|
||||
table: "ServerHttpPath",
|
||||
column: "CreatedById");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_ServerHttpPath_ServerId",
|
||||
table: "ServerHttpPath",
|
||||
column: "ServerId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_ServerHttpPath_ServerId1",
|
||||
table: "ServerHttpPath",
|
||||
column: "ServerId1");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_ServerHttpPath_UpdatedById",
|
||||
table: "ServerHttpPath",
|
||||
column: "UpdatedById");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "ServerHttpPath");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -859,6 +859,51 @@ namespace LANCommander.Migrations
|
|||
b.ToTable("ServerConsoles");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LANCommander.Data.Models.ServerHttpPath", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<Guid?>("CreatedById")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("CreatedOn")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("LocalPath")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Path")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<Guid>("ServerId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<Guid?>("ServerId1")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<Guid?>("UpdatedById")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("UpdatedOn")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("CreatedById");
|
||||
|
||||
b.HasIndex("ServerId");
|
||||
|
||||
b.HasIndex("ServerId1");
|
||||
|
||||
b.HasIndex("UpdatedById");
|
||||
|
||||
b.ToTable("ServerHttpPath");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LANCommander.Data.Models.Tag", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
|
@ -1507,6 +1552,33 @@ namespace LANCommander.Migrations
|
|||
b.Navigation("UpdatedBy");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LANCommander.Data.Models.ServerHttpPath", b =>
|
||||
{
|
||||
b.HasOne("LANCommander.Data.Models.User", "CreatedBy")
|
||||
.WithMany()
|
||||
.HasForeignKey("CreatedById");
|
||||
|
||||
b.HasOne("LANCommander.Data.Models.Server", "Server")
|
||||
.WithMany()
|
||||
.HasForeignKey("ServerId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("LANCommander.Data.Models.Server", null)
|
||||
.WithMany("HttpPaths")
|
||||
.HasForeignKey("ServerId1");
|
||||
|
||||
b.HasOne("LANCommander.Data.Models.User", "UpdatedBy")
|
||||
.WithMany()
|
||||
.HasForeignKey("UpdatedById");
|
||||
|
||||
b.Navigation("CreatedBy");
|
||||
|
||||
b.Navigation("Server");
|
||||
|
||||
b.Navigation("UpdatedBy");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LANCommander.Data.Models.Tag", b =>
|
||||
{
|
||||
b.HasOne("LANCommander.Data.Models.User", "CreatedBy")
|
||||
|
@ -1608,6 +1680,8 @@ namespace LANCommander.Migrations
|
|||
|
||||
modelBuilder.Entity("LANCommander.Data.Models.Server", b =>
|
||||
{
|
||||
b.Navigation("HttpPaths");
|
||||
|
||||
b.Navigation("ServerConsoles");
|
||||
});
|
||||
|
||||
|
|
|
@ -0,0 +1,83 @@
|
|||
@using LANCommander.Data.Enums
|
||||
@using LANCommander.Data.Models
|
||||
@using LANCommander.Extensions;
|
||||
|
||||
<Space Direction="DirectionVHType.Vertical" Size="@("large")" Style="width: 100%">
|
||||
@if (Values == null || Values.Count == 0)
|
||||
{
|
||||
<Empty />
|
||||
}
|
||||
|
||||
@foreach (var httpPath in Values)
|
||||
{
|
||||
<SpaceItem>
|
||||
<Table TItem="ServerHttpPath" DataSource="@Values" HidePagination="true" Responsive>
|
||||
<PropertyColumn Property="p => p.LocalPath" Title="Local Path">
|
||||
<FilePicker @bind-Value="context.LocalPath" AllowDirectories Title="Select Local Path" Root="@WorkingDirectory" />
|
||||
</PropertyColumn>
|
||||
<PropertyColumn Property="p => p.Path">
|
||||
<Input Type="text" @bind-Value="context.Path" />
|
||||
</PropertyColumn>
|
||||
<ActionColumn>
|
||||
<Space Style="display: flex; justify-content: end">
|
||||
<SpaceItem>
|
||||
<Button OnClick="() => Remove(context)" Type="@ButtonType.Text" Danger Icon="@IconType.Outline.Close" />
|
||||
</SpaceItem>
|
||||
</Space>
|
||||
</ActionColumn>
|
||||
</Table>
|
||||
</SpaceItem>
|
||||
}
|
||||
|
||||
<SpaceItem>
|
||||
<GridRow Justify="end">
|
||||
<GridCol>
|
||||
<Button OnClick="Add" Type="@ButtonType.Primary">Add Path</Button>
|
||||
</GridCol>
|
||||
</GridRow>
|
||||
</SpaceItem>
|
||||
</Space>
|
||||
|
||||
@code {
|
||||
[Parameter] public ICollection<ServerHttpPath> Values { get; set; }
|
||||
[Parameter] public EventCallback<ICollection<ServerHttpPath>> ValuesChanged { get; set; }
|
||||
[Parameter] public Guid ServerId { get; set; }
|
||||
[Parameter] public string WorkingDirectory { get; set; }
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
if (Values == null)
|
||||
Values = new List<ServerHttpPath>();
|
||||
|
||||
if (ValuesChanged.HasDelegate)
|
||||
await ValuesChanged.InvokeAsync(Values);
|
||||
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
private async Task Add()
|
||||
{
|
||||
if (Values == null)
|
||||
Values = new List<ServerHttpPath>();
|
||||
|
||||
Values.Add(new ServerHttpPath
|
||||
{
|
||||
ServerId = ServerId
|
||||
});
|
||||
|
||||
if (ValuesChanged.HasDelegate)
|
||||
await ValuesChanged.InvokeAsync(Values);
|
||||
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
private async Task Remove(ServerHttpPath httpPath)
|
||||
{
|
||||
Values.Remove(httpPath);
|
||||
|
||||
if (ValuesChanged.HasDelegate)
|
||||
await ValuesChanged.InvokeAsync(Values);
|
||||
|
||||
StateHasChanged();
|
||||
}
|
||||
}
|
|
@ -118,19 +118,9 @@
|
|||
|
||||
@if (Panel == "HTTP")
|
||||
{
|
||||
<Form Model="@Server" Layout="@FormLayout.Vertical">
|
||||
<FormItem Label="Enable HTTP">
|
||||
<Switch @bind-Checked="context.EnableHTTP" />
|
||||
</FormItem>
|
||||
<ServerHttpPathEditor @bind-Values="Server.HttpPaths" ServerId="Id" WorkingDirectory="@Server.WorkingDirectory" />
|
||||
|
||||
<FormItem Label="Root Path">
|
||||
<FilePicker Root="@RootPath" Title="Choose Root Path" OkText="Select Directory" EntrySelectable="x => x is FileManagerDirectory" @bind-Value="@context.HTTPRootPath" />
|
||||
</FormItem>
|
||||
|
||||
<FormItem>
|
||||
<Button Type="@ButtonType.Primary" OnClick="Save" Icon="@IconType.Fill.Save">Save</Button>
|
||||
</FormItem>
|
||||
</Form>
|
||||
<Button Type="@ButtonType.Primary" OnClick="Save" Icon="@IconType.Fill.Save">Save</Button>
|
||||
}
|
||||
|
||||
@if (Panel == "Monitor")
|
||||
|
@ -180,6 +170,9 @@
|
|||
if (Server.GameId.HasValue)
|
||||
GameId = Server.GameId.Value;
|
||||
|
||||
if (Server.HttpPaths == null)
|
||||
Server.HttpPaths = new List<ServerHttpPath>();
|
||||
|
||||
Games = await GameService.Get();
|
||||
}
|
||||
|
||||
|
@ -210,6 +203,14 @@
|
|||
}
|
||||
}
|
||||
|
||||
private async Task AddPath()
|
||||
{
|
||||
Server.HttpPaths.Add(new ServerHttpPath()
|
||||
{
|
||||
ServerId = Server.Id
|
||||
});
|
||||
}
|
||||
|
||||
private void OnPathSelected(string path)
|
||||
{
|
||||
Server.WorkingDirectory = Path.GetDirectoryName(path);
|
||||
|
|
Loading…
Reference in New Issue