Remotely/Server/Startup.cs
2021-07-29 07:53:58 -07:00

282 lines
10 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Remotely.Server.Data;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.AspNetCore.Identity.UI.Services;
using System.IO;
using Remotely.Server.Services;
using Microsoft.Extensions.FileProviders;
using Microsoft.AspNetCore.StaticFiles;
using Microsoft.AspNetCore.SignalR;
using Remotely.Shared.Models;
using Microsoft.AspNetCore.Http.Connections;
using Remotely.Shared.Services;
using Microsoft.AspNetCore.HttpOverrides;
using Microsoft.AspNetCore.Identity.UI;
using Microsoft.AspNetCore.Cors.Infrastructure;
using Newtonsoft.Json;
using System.Net;
using Microsoft.Extensions.Hosting;
using Swashbuckle.AspNetCore.Swagger;
using Microsoft.OpenApi.Models;
namespace Remotely.Server
{
public class Startup
{
public Startup(IConfiguration configuration, IWebHostEnvironment env)
{
Configuration = configuration;
IsDev = env.IsDevelopment();
}
public IConfiguration Configuration { get; }
private bool IsDev { get; set; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.Configure<CookiePolicyOptions>(options =>
{
// This lambda determines whether user consent for non-essential cookies is needed for a given request.
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
var dbProvider = Configuration["ApplicationOptions:DBProvider"].ToLower();
if (dbProvider == "sqlite")
{
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlite(
Configuration.GetConnectionString("SQLite")));
}
else if (dbProvider == "sqlserver")
{
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(
Configuration.GetConnectionString("SQLServer")));
}
else if (dbProvider == "postgresql")
{
services.AddDbContext<ApplicationDbContext>(options =>
options.UseNpgsql(
Configuration.GetConnectionString("PostgreSQL")));
}
services.AddIdentity<RemotelyUser, IdentityRole>(options => options.Stores.MaxLengthForKeys = 128)
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultUI()
.AddDefaultTokenProviders();
var remoteControlAuthentication = Configuration.GetSection("ApplicationOptions:RemoteControlRequiresAuthentication").Get<bool>();
services.AddAuthorization(options =>
{
options.AddPolicy("RemoteControlPolicy", policy =>
{
if (remoteControlAuthentication)
{
policy.RequireAuthenticatedUser();
}
else
{
policy.RequireAssertion((context) => true);
}
policy.Build();
});
});
services.ConfigureApplicationCookie(cookieOptions =>
{
cookieOptions.Cookie.SameSite = SameSiteMode.None;
});
var trustedOrigins = Configuration.GetSection("ApplicationOptions:TrustedCorsOrigins").Get<string[]>();
if (trustedOrigins != null)
{
services.AddCors(options =>
{
options.AddPolicy("TrustedOriginPolicy", builder => builder
.WithOrigins(trustedOrigins)
.AllowAnyHeader()
.AllowAnyMethod()
.AllowCredentials()
);
});
}
var knownProxies = Configuration.GetSection("ApplicationOptions:KnownProxies").Get<string[]>();
if (knownProxies != null)
{
services.Configure<ForwardedHeadersOptions>(options =>
{
foreach (var proxy in knownProxies)
{
options.KnownProxies.Add(IPAddress.Parse(proxy));
options.ForwardLimit = 2;
}
});
}
services.AddMvc()
.SetCompatibilityVersion(CompatibilityVersion.Version_3_0).AddJsonOptions(options =>
{
options.JsonSerializerOptions.PropertyNamingPolicy = new PascalCase();
});
services.AddSignalR(options =>
{
options.EnableDetailedErrors = IsDev;
options.MaximumReceiveMessageSize = 20000000;
})
.AddJsonProtocol(options =>
{
options.PayloadSerializerOptions.PropertyNamingPolicy = new PascalCase();
})
.AddMessagePackProtocol();
// TODO: Re-enable when Swagger works when building for Linux on Windows.
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "Remotely API", Version = "v1" });
});
services.AddLogging();
services.AddScoped<IEmailSender, EmailSender>();
services.AddScoped<EmailSender>();
services.AddScoped<DataService>();
services.AddScoped<RemoteControlSessionRecorder>();
services.AddSingleton<ApplicationConfig>();
services.AddSingleton<RandomGenerator>();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ApplicationDbContext context, DataService dataService)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
if (bool.Parse(Configuration["ApplicationOptions:UseHSTS"]))
{
app.UseHsts();
}
if (bool.Parse(Configuration["ApplicationOptions:RedirectToHTTPS"]))
{
app.UseHttpsRedirection();
}
}
ConfigureStaticFiles(app);
app.UseCookiePolicy();
app.UseForwardedHeaders(new ForwardedHeadersOptions
{
ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
});
// TODO: Re-enable when Swagger works when building for Linux on Windows.
app.UseSwagger();
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "Remotely API V1");
});
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(routeBuilder =>
{
routeBuilder.MapHub<BrowserSocketHub>("/BrowserHub", options =>
{
options.ApplicationMaxBufferSize = 500000;
options.TransportMaxBufferSize = 500000;
});
routeBuilder.MapHub<DeviceSocketHub>("/DeviceHub", options =>
{
options.ApplicationMaxBufferSize = 500000;
options.TransportMaxBufferSize = 500000;
});
routeBuilder.MapHub<RCDeviceSocketHub>("/RCDeviceHub", options =>
{
options.ApplicationMaxBufferSize = 2000000;
options.TransportMaxBufferSize = 2000000;
});
routeBuilder.MapHub<RCBrowserSocketHub>("/RCBrowserHub", options =>
{
options.ApplicationMaxBufferSize = 2000000;
options.TransportMaxBufferSize = 2000000;
});
routeBuilder.MapRazorPages();
routeBuilder.MapControllers();
});
app.UseCors("TrustedOriginPolicy");
try
{
context.Database.Migrate();
}
catch (Exception ex)
{
dataService.WriteEvent(ex);
}
dataService.SetAllDevicesNotOnline();
dataService.CleanupOldRecords();
}
private void ConfigureStaticFiles(IApplicationBuilder app)
{
var provider = new FileExtensionContentTypeProvider();
// Add new mappings
provider.Mappings[".ps1"] = "application/octet-stream";
provider.Mappings[".exe"] = "application/octet-stream";
provider.Mappings[".dll"] = "application/octet-stream";
provider.Mappings[".appimage"] = "application/octet-stream";
provider.Mappings[".zip"] = "application/octet-stream";
provider.Mappings[".config"] = "application/octet-stream";
app.UseStaticFiles();
app.UseStaticFiles(new StaticFileOptions()
{
FileProvider = new PhysicalFileProvider(Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "Downloads")),
RequestPath = new PathString("/Downloads"),
ContentTypeProvider = provider,
DefaultContentType = "application/octet-stream"
});
// Needed for Let's Encrypt.
if (Directory.Exists(Path.Combine(Directory.GetCurrentDirectory(), ".well-known")))
{
app.UseStaticFiles(new StaticFileOptions
{
FileProvider = new PhysicalFileProvider(Path.Combine(Directory.GetCurrentDirectory(), @".well-known")),
RequestPath = new PathString("/.well-known"),
ServeUnknownFileTypes = true
});
}
}
}
}