diff --git a/MareSynchronosServer/MareSynchronos.API/MareSynchronos.API.csproj b/MareSynchronosServer/MareSynchronos.API/MareSynchronos.API.csproj
new file mode 100644
index 0000000..f208d30
--- /dev/null
+++ b/MareSynchronosServer/MareSynchronos.API/MareSynchronos.API.csproj
@@ -0,0 +1,7 @@
+
+
+
+ net5.0
+
+
+
diff --git a/MareSynchronosServer/MareSynchronos.API/WhitelistDto.cs b/MareSynchronosServer/MareSynchronos.API/WhitelistDto.cs
new file mode 100644
index 0000000..a46a9db
--- /dev/null
+++ b/MareSynchronosServer/MareSynchronos.API/WhitelistDto.cs
@@ -0,0 +1,10 @@
+namespace MareSynchronos.API
+{
+ public class WhitelistDto
+ {
+ public string OtherUID { get; set; }
+ public bool IsPaused { get; set; }
+ public bool IsSynced { get; set; }
+ public bool IsPausedFromOthers { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/MareSynchronosServer/MareSynchronosServer.sln b/MareSynchronosServer/MareSynchronosServer.sln
new file mode 100644
index 0000000..c345141
--- /dev/null
+++ b/MareSynchronosServer/MareSynchronosServer.sln
@@ -0,0 +1,31 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.2.32602.215
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MareSynchronosServer", "MareSynchronosServer\MareSynchronosServer.csproj", "{029CA97F-E0BA-4172-A191-EA21FB61AD0F}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MareSynchronos.API", "MareSynchronos.API\MareSynchronos.API.csproj", "{E1D8937E-26D0-46FF-8EDA-F35BF8AEFF1D}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {029CA97F-E0BA-4172-A191-EA21FB61AD0F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {029CA97F-E0BA-4172-A191-EA21FB61AD0F}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {029CA97F-E0BA-4172-A191-EA21FB61AD0F}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {029CA97F-E0BA-4172-A191-EA21FB61AD0F}.Release|Any CPU.Build.0 = Release|Any CPU
+ {E1D8937E-26D0-46FF-8EDA-F35BF8AEFF1D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {E1D8937E-26D0-46FF-8EDA-F35BF8AEFF1D}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {E1D8937E-26D0-46FF-8EDA-F35BF8AEFF1D}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {E1D8937E-26D0-46FF-8EDA-F35BF8AEFF1D}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {78C476A5-6E88-449B-828D-A2465D9D3295}
+ EndGlobalSection
+EndGlobal
diff --git a/MareSynchronosServer/MareSynchronosServer/Authentication/IdBasedUserIdProvider.cs b/MareSynchronosServer/MareSynchronosServer/Authentication/IdBasedUserIdProvider.cs
new file mode 100644
index 0000000..05aef38
--- /dev/null
+++ b/MareSynchronosServer/MareSynchronosServer/Authentication/IdBasedUserIdProvider.cs
@@ -0,0 +1,14 @@
+using System.Linq;
+using System.Security.Claims;
+using Microsoft.AspNetCore.SignalR;
+
+namespace MareSynchronosServer.Authentication
+{
+ public class IdBasedUserIdProvider : IUserIdProvider
+ {
+ public string GetUserId(HubConnectionContext context)
+ {
+ return context.User!.Claims.SingleOrDefault(c => c.Type == ClaimTypes.NameIdentifier)?.Value;
+ }
+ }
+}
diff --git a/MareSynchronosServer/MareSynchronosServer/Authentication/SecretKeyAuthenticationHandler.cs b/MareSynchronosServer/MareSynchronosServer/Authentication/SecretKeyAuthenticationHandler.cs
new file mode 100644
index 0000000..98210be
--- /dev/null
+++ b/MareSynchronosServer/MareSynchronosServer/Authentication/SecretKeyAuthenticationHandler.cs
@@ -0,0 +1,58 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Security.Claims;
+using System.Security.Cryptography;
+using System.Text;
+using System.Text.Encodings.Web;
+using System.Threading.Tasks;
+using MareSynchronosServer.Data;
+using Microsoft.AspNetCore.Authentication;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Options;
+using Microsoft.IdentityModel.Tokens;
+
+namespace MareSynchronosServer.Authentication
+{
+ public class SecretKeyAuthenticationHandler : AuthenticationHandler
+ {
+ private readonly MareDbContext _mareDbContext;
+ public const string AUTH_SCHEME = "SecretKeyAuth";
+
+ protected override Task HandleAuthenticateAsync()
+ {
+ if (!Request.Headers.ContainsKey("Authorization"))
+ return Task.FromResult(AuthenticateResult.Fail("Failed Authorization"));
+
+ var authHeader = Request.Headers["Authorization"].ToString();
+
+ if (string.IsNullOrEmpty(authHeader))
+ return Task.FromResult(AuthenticateResult.Fail("Failed Authorization"));
+
+ using var sha256 = SHA256.Create();
+ var hashedHeader = BitConverter.ToString(sha256.ComputeHash(Encoding.UTF8.GetBytes(authHeader))).Replace("-", "");
+ var user = _mareDbContext.Users.SingleOrDefault(m => m.SecretKey == hashedHeader);
+
+ if (user == null)
+ {
+ return Task.FromResult(AuthenticateResult.Fail("Failed Authorization"));
+ }
+
+ var claims = new List {
+ new Claim(ClaimTypes.Name, user.CharacterIdentification ?? "Unknown"),
+ new Claim(ClaimTypes.NameIdentifier, user.UID)
+ };
+
+ var identity = new ClaimsIdentity(claims, nameof(SecretKeyAuthenticationHandler));
+ var principal = new ClaimsPrincipal(identity);
+ var ticket = new AuthenticationTicket(principal, this.Scheme.Name);
+
+ return Task.FromResult(AuthenticateResult.Success(ticket));
+ }
+
+ public SecretKeyAuthenticationHandler(IOptionsMonitor options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock, MareDbContext mareDbContext) : base(options, logger, encoder, clock)
+ {
+ _mareDbContext = mareDbContext;
+ }
+ }
+}
diff --git a/MareSynchronosServer/MareSynchronosServer/Data/MareDbContext.cs b/MareSynchronosServer/MareSynchronosServer/Data/MareDbContext.cs
new file mode 100644
index 0000000..ad00f9d
--- /dev/null
+++ b/MareSynchronosServer/MareSynchronosServer/Data/MareDbContext.cs
@@ -0,0 +1,23 @@
+using MareSynchronosServer.Models;
+using Microsoft.EntityFrameworkCore;
+
+namespace MareSynchronosServer.Data
+{
+ public class MareDbContext : Microsoft.EntityFrameworkCore.DbContext
+ {
+ public MareDbContext(DbContextOptions options) : base(options)
+ {
+ }
+
+ public DbSet Users { get; set; }
+ public DbSet Files { get; set; }
+ public DbSet Whitelists { get; set; }
+
+ protected override void OnModelCreating(ModelBuilder modelBuilder)
+ {
+ modelBuilder.Entity().ToTable("Users");
+ modelBuilder.Entity().ToTable("FileCaches");
+ modelBuilder.Entity().ToTable("Whitelists");
+ }
+ }
+}
diff --git a/MareSynchronosServer/MareSynchronosServer/Hubs/Connection.cs b/MareSynchronosServer/MareSynchronosServer/Hubs/Connection.cs
new file mode 100644
index 0000000..745b51a
--- /dev/null
+++ b/MareSynchronosServer/MareSynchronosServer/Hubs/Connection.cs
@@ -0,0 +1,21 @@
+using System.Diagnostics;
+using System.Linq;
+using System.Security.Claims;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.SignalR;
+
+namespace MareSynchronosServer.Hubs
+{
+ public class Connection : Hub
+ {
+ public string Heartbeat()
+ {
+ var userId = Context.User!.Claims.SingleOrDefault(c => c.Type == ClaimTypes.NameIdentifier)?.Value;
+ if (userId != null)
+ {
+ var user = Clients.User(userId);
+ }
+ return userId ?? string.Empty;
+ }
+ }
+}
diff --git a/MareSynchronosServer/MareSynchronosServer/Hubs/Files.cs b/MareSynchronosServer/MareSynchronosServer/Hubs/Files.cs
new file mode 100644
index 0000000..d84b5e1
--- /dev/null
+++ b/MareSynchronosServer/MareSynchronosServer/Hubs/Files.cs
@@ -0,0 +1,75 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Security.Claims;
+using System.Security.Cryptography;
+using System.Threading.Tasks;
+using MareSynchronosServer.Authentication;
+using MareSynchronosServer.Data;
+using MareSynchronosServer.Models;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.SignalR;
+
+namespace MareSynchronosServer.Hubs
+{
+ [Authorize(AuthenticationSchemes = SecretKeyAuthenticationHandler.AUTH_SCHEME)]
+ public class Files : Hub
+ {
+ private readonly MareDbContext _dbContext;
+
+ public Files(MareDbContext dbContext)
+ {
+ _dbContext = dbContext;
+ }
+
+ [Authorize(AuthenticationSchemes = SecretKeyAuthenticationHandler.AUTH_SCHEME)]
+ public async Task SendFiles(List fileList)
+ {
+ var existingFiles = _dbContext.Files.Where(f => fileList.Contains(f.Hash)).ToList();
+ foreach (var file in fileList.Where(f => existingFiles.All(e => e.Hash != f)))
+ {
+ var userId = Context.User!.Claims.SingleOrDefault(c => c.Type == ClaimTypes.NameIdentifier)?.Value ?? "Unknown";
+ await _dbContext.Files.AddAsync(new FileCache()
+ {
+ Hash = file,
+ LastAccessTime = DateTime.Now,
+ Uploaded = false,
+ Uploader = _dbContext.Users.Single(u => u.UID == userId)
+ });
+ await _dbContext.SaveChangesAsync();
+ await Clients.Caller!.SendAsync("FileRequest", file);
+ }
+ }
+
+ [Authorize(AuthenticationSchemes = SecretKeyAuthenticationHandler.AUTH_SCHEME)]
+ public async Task UploadFile(string hash, byte[] file)
+ {
+ var relatedFile = _dbContext.Files.SingleOrDefault(f => f.Hash == hash);
+ if (relatedFile == null) return false;
+ var decodedFile = LZ4.LZ4Codec.Unwrap(file);
+ using var sha1 = new SHA1CryptoServiceProvider();
+ var computedHash = await sha1.ComputeHashAsync(new MemoryStream(decodedFile));
+ var computedHashString = BitConverter.ToString(computedHash).Replace("-", "");
+ if (hash != computedHashString)
+ {
+ return false;
+ }
+ await File.WriteAllBytesAsync(@"G:\ServerTest\" + hash, file);
+ relatedFile.Uploaded = true;
+ relatedFile.LastAccessTime = DateTime.Now;
+ await _dbContext.SaveChangesAsync();
+ return true;
+ }
+
+ public override Task OnDisconnectedAsync(Exception exception)
+ {
+ var userId = Context.User!.Claims.SingleOrDefault(c => c.Type == ClaimTypes.NameIdentifier)?.Value;
+ var notUploadedFiles = _dbContext.Files.Where(f => !f.Uploaded && f.Uploader.UID == userId).ToList();
+ _dbContext.RemoveRange(notUploadedFiles);
+ _dbContext.SaveChanges();
+ return base.OnDisconnectedAsync(exception);
+ }
+ }
+}
diff --git a/MareSynchronosServer/MareSynchronosServer/Hubs/User.cs b/MareSynchronosServer/MareSynchronosServer/Hubs/User.cs
new file mode 100644
index 0000000..48f762e
--- /dev/null
+++ b/MareSynchronosServer/MareSynchronosServer/Hubs/User.cs
@@ -0,0 +1,157 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Linq;
+using System.Security.Claims;
+using System.Security.Cryptography;
+using System.Text;
+using System.Threading.Tasks;
+using MareSynchronos.API;
+using MareSynchronosServer.Authentication;
+using MareSynchronosServer.Data;
+using MareSynchronosServer.Models;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.SignalR;
+using Microsoft.EntityFrameworkCore;
+
+namespace MareSynchronosServer.Hubs
+{
+ public class User : Hub
+ {
+ private readonly MareDbContext _dbContext;
+
+ public User(MareDbContext dbContext)
+ {
+ _dbContext = dbContext;
+ }
+
+ public async Task Register()
+ {
+ using var sha256 = SHA256.Create();
+ var computedHash = BitConverter.ToString(sha256.ComputeHash(Encoding.UTF8.GetBytes(GenerateRandomString(64)))).Replace("-", "");
+ var user = new Models.User
+ {
+ SecretKey = BitConverter.ToString(sha256.ComputeHash(Encoding.UTF8.GetBytes(computedHash)))
+ .Replace("-", ""),
+ };
+
+ var hasValidUid = false;
+ while (!hasValidUid)
+ {
+ var uid = GenerateRandomString(10);
+ if (_dbContext.Users.Any(u => u.UID == uid)) continue;
+ user.UID = uid;
+ hasValidUid = true;
+ }
+ _dbContext.Users.Add(user);
+
+ await _dbContext.SaveChangesAsync();
+ return computedHash;
+ }
+
+ [Authorize(AuthenticationSchemes = SecretKeyAuthenticationHandler.AUTH_SCHEME)]
+ public string GetUID()
+ {
+ return Context.User!.Claims.Single(c => c.Type == ClaimTypes.NameIdentifier).Value;
+ }
+
+ [Authorize(AuthenticationSchemes = SecretKeyAuthenticationHandler.AUTH_SCHEME)]
+ public async Task SendWhitelist(List whiteListEntries)
+ {
+ var currentUserId = Context.User!.Claims.Single(c => c.Type == ClaimTypes.NameIdentifier).Value;
+ var user = _dbContext.Users.Single(u => u.UID == currentUserId);
+ var userWhitelists = _dbContext.Whitelists
+ .Include(w => w.User)
+ .Include(w => w.OtherUser)
+ .Where(w => w.User.UID == currentUserId)
+ .ToList();
+ foreach (var whitelist in whiteListEntries)
+ {
+ var otherEntry = _dbContext.Whitelists.SingleOrDefault(w =>
+ w.User.UID == whitelist.OtherUID && w.OtherUser.UID == user.UID);
+
+ var prevEntry = userWhitelists.SingleOrDefault(w => w.OtherUser.UID == whitelist.OtherUID);
+ if (prevEntry != null)
+ {
+ prevEntry.IsPaused = whitelist.IsPaused;
+ }
+ else
+ {
+ var otherUser = _dbContext.Users.SingleOrDefault(u => u.UID == whitelist.OtherUID);
+ if (otherUser != null)
+ {
+ Whitelist wl = new Whitelist
+ {
+ User = user,
+ OtherUser = otherUser,
+ IsPaused = whitelist.IsPaused
+ };
+ otherEntry = wl;
+ await _dbContext.Whitelists.AddAsync(wl);
+ }
+ }
+
+ if (otherEntry != null)
+ {
+ await Clients.User(whitelist.OtherUID).SendAsync("UpdateWhitelist", currentUserId, true,
+ whitelist.IsPaused);
+ }
+
+ await _dbContext.SaveChangesAsync();
+ }
+
+ foreach (var deletedEntry in userWhitelists.Where(u => whiteListEntries.All(e => e.OtherUID != u.OtherUser.UID)).ToList())
+ {
+ var otherEntry = _dbContext.Whitelists.SingleOrDefault(w =>
+ w.User.UID == deletedEntry.OtherUser.UID && w.OtherUser.UID == user.UID);
+ if (otherEntry != null)
+ {
+ await Clients.User(otherEntry.User.UID).SendAsync("UpdateWhitelist", currentUserId, false, false);
+ }
+
+ _dbContext.Whitelists.Remove(deletedEntry);
+ }
+ _dbContext.Whitelists.RemoveRange();
+ await _dbContext.SaveChangesAsync();
+ }
+
+ [Authorize(AuthenticationSchemes = SecretKeyAuthenticationHandler.AUTH_SCHEME)]
+ public async Task> GetWhitelist()
+ {
+ string userid = Context.User!.Claims.Single(c => c.Type == ClaimTypes.NameIdentifier).Value;
+ return _dbContext.Whitelists.Include(u => u.OtherUser).Include(u => u.User).Where(w => w.User.UID == userid)
+ .ToList()
+ .Select(w =>
+ {
+ var otherEntry = _dbContext.Whitelists.SingleOrDefault(a => a.User.UID == w.OtherUser.UID && a.OtherUser.UID == userid);
+ return new WhitelistDto
+ {
+ IsPaused = w.IsPaused,
+ OtherUID = w.OtherUser.UID,
+ IsSynced = otherEntry != null,
+ IsPausedFromOthers = otherEntry?.IsPaused ?? false
+ };
+ }).ToList();
+ }
+
+ public static string GenerateRandomString(int length, string allowableChars = null)
+ {
+ if (string.IsNullOrEmpty(allowableChars))
+ allowableChars = @"ABCDEFGHJKLMNPQRSTUVWXYZ0123456789";
+
+ // Generate random data
+ var rnd = new byte[length];
+ using (var rng = new RNGCryptoServiceProvider())
+ rng.GetBytes(rnd);
+
+ // Generate the output string
+ var allowable = allowableChars.ToCharArray();
+ var l = allowable.Length;
+ var chars = new char[length];
+ for (var i = 0; i < length; i++)
+ chars[i] = allowable[rnd[i] % l];
+
+ return new string(chars);
+ }
+ }
+}
diff --git a/MareSynchronosServer/MareSynchronosServer/MareSynchronosServer.csproj b/MareSynchronosServer/MareSynchronosServer/MareSynchronosServer.csproj
new file mode 100644
index 0000000..ebb579b
--- /dev/null
+++ b/MareSynchronosServer/MareSynchronosServer/MareSynchronosServer.csproj
@@ -0,0 +1,26 @@
+
+
+
+ net5.0
+ aspnet-MareSynchronosServer-BA82A12A-0B30-463C-801D-B7E81318CD50
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/MareSynchronosServer/MareSynchronosServer/Models/FileCache.cs b/MareSynchronosServer/MareSynchronosServer/Models/FileCache.cs
new file mode 100644
index 0000000..95c3297
--- /dev/null
+++ b/MareSynchronosServer/MareSynchronosServer/Models/FileCache.cs
@@ -0,0 +1,17 @@
+using System;
+using System.ComponentModel.DataAnnotations;
+using System.ComponentModel.DataAnnotations.Schema;
+
+namespace MareSynchronosServer.Models
+{
+ public class FileCache
+ {
+ [Key]
+ public string Hash { get; set; }
+ public User Uploader { get; set; }
+ public bool Uploaded { get; set; }
+ public DateTime LastAccessTime { get; set; }
+ [Timestamp]
+ public byte[] Timestamp { get; set; }
+ }
+}
diff --git a/MareSynchronosServer/MareSynchronosServer/Models/User.cs b/MareSynchronosServer/MareSynchronosServer/Models/User.cs
new file mode 100644
index 0000000..af22949
--- /dev/null
+++ b/MareSynchronosServer/MareSynchronosServer/Models/User.cs
@@ -0,0 +1,15 @@
+using System;
+using System.ComponentModel.DataAnnotations;
+
+namespace MareSynchronosServer.Models
+{
+ public class User
+ {
+ [Key]
+ public string UID { get; set; }
+ public string SecretKey { get; set; }
+ public string CharacterIdentification { get; set; }
+ [Timestamp]
+ public byte[] Timestamp { get; set; }
+ }
+}
diff --git a/MareSynchronosServer/MareSynchronosServer/Models/Whitelist.cs b/MareSynchronosServer/MareSynchronosServer/Models/Whitelist.cs
new file mode 100644
index 0000000..fc1290f
--- /dev/null
+++ b/MareSynchronosServer/MareSynchronosServer/Models/Whitelist.cs
@@ -0,0 +1,14 @@
+using System.ComponentModel.DataAnnotations;
+
+namespace MareSynchronosServer.Models
+{
+ public class Whitelist
+ {
+ public int Id { get; set; }
+ public User User { get; set; }
+ public User OtherUser { get; set; }
+ public bool IsPaused { get; set; }
+ [Timestamp]
+ public byte[] Timestamp { get; set; }
+ }
+}
diff --git a/MareSynchronosServer/MareSynchronosServer/Program.cs b/MareSynchronosServer/MareSynchronosServer/Program.cs
new file mode 100644
index 0000000..b69fe88
--- /dev/null
+++ b/MareSynchronosServer/MareSynchronosServer/Program.cs
@@ -0,0 +1,39 @@
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.Hosting;
+using Microsoft.Extensions.Logging;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using MareSynchronosServer.Data;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace MareSynchronosServer
+{
+ public class Program
+ {
+ public static void Main(string[] args)
+ {
+ var host = CreateHostBuilder(args).Build();
+
+ using (var scope = host.Services.CreateScope())
+ {
+ var services = scope.ServiceProvider;
+ var context = services.GetRequiredService();
+ context.Database.EnsureCreated();
+ }
+
+ host.Run();
+ }
+
+ // Server=localhost\SQLEXPRESS;Database=master;Trusted_Connection=True;
+
+ public static IHostBuilder CreateHostBuilder(string[] args) =>
+ Host.CreateDefaultBuilder(args)
+ .ConfigureWebHostDefaults(webBuilder =>
+ {
+ webBuilder.UseStartup();
+ });
+ }
+}
diff --git a/MareSynchronosServer/MareSynchronosServer/Properties/launchSettings.json b/MareSynchronosServer/MareSynchronosServer/Properties/launchSettings.json
new file mode 100644
index 0000000..f9f59b7
--- /dev/null
+++ b/MareSynchronosServer/MareSynchronosServer/Properties/launchSettings.json
@@ -0,0 +1,21 @@
+{
+ "iisSettings": {
+ "windowsAuthentication": false,
+ "anonymousAuthentication": true,
+ "iisExpress": {
+ "applicationUrl": "http://localhost:43136",
+ "sslPort": 44364
+ }
+ },
+ "profiles": {
+ "MareSynchronosServer": {
+ "commandName": "Project",
+ "dotnetRunMessages": "true",
+ "launchBrowser": false,
+ "applicationUrl": "https://localhost:5001;http://localhost:5000;https://darkarchon.internet-box.ch:5001;http://darkarchon.internet-box.ch:5000",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ }
+ }
+}
diff --git a/MareSynchronosServer/MareSynchronosServer/Properties/serviceDependencies.json b/MareSynchronosServer/MareSynchronosServer/Properties/serviceDependencies.json
new file mode 100644
index 0000000..7d43f21
--- /dev/null
+++ b/MareSynchronosServer/MareSynchronosServer/Properties/serviceDependencies.json
@@ -0,0 +1,13 @@
+{
+ "dependencies": {
+ "mssql1": {
+ "type": "mssql",
+ "connectionId": "ConnectionStrings:DefaultConnection",
+ "dynamicId": null
+ },
+ "secrets1": {
+ "type": "secrets",
+ "connectionId": null
+ }
+ }
+}
\ No newline at end of file
diff --git a/MareSynchronosServer/MareSynchronosServer/Properties/serviceDependencies.local.json b/MareSynchronosServer/MareSynchronosServer/Properties/serviceDependencies.local.json
new file mode 100644
index 0000000..7aa7fcd
--- /dev/null
+++ b/MareSynchronosServer/MareSynchronosServer/Properties/serviceDependencies.local.json
@@ -0,0 +1,16 @@
+{
+ "dependencies": {
+ "mssql1": {
+ "secretStore": "LocalSecretsFile",
+ "type": "mssql.onprem",
+ "connectionId": "ConnectionStrings:DefaultConnection",
+ "dynamicId": null
+ },
+ "secrets1": {
+ "secretStore": null,
+ "resourceId": null,
+ "type": "secrets.user",
+ "connectionId": null
+ }
+ }
+}
\ No newline at end of file
diff --git a/MareSynchronosServer/MareSynchronosServer/Startup.cs b/MareSynchronosServer/MareSynchronosServer/Startup.cs
new file mode 100644
index 0000000..2c73d6f
--- /dev/null
+++ b/MareSynchronosServer/MareSynchronosServer/Startup.cs
@@ -0,0 +1,93 @@
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Components;
+using Microsoft.AspNetCore.Components.Authorization;
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.AspNetCore.HttpsPolicy;
+using Microsoft.AspNetCore.Identity;
+using Microsoft.AspNetCore.Identity.UI;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Hosting;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Threading.Tasks;
+using Bazinga.AspNetCore.Authentication.Basic;
+using MareSynchronosServer.Authentication;
+using MareSynchronosServer.Data;
+using MareSynchronosServer.Hubs;
+using Microsoft.AspNetCore.Authentication;
+using Microsoft.AspNetCore.ResponseCompression;
+using Microsoft.AspNetCore.SignalR;
+
+namespace MareSynchronosServer
+{
+ public class Startup
+ {
+ public Startup(IConfiguration configuration)
+ {
+ Configuration = configuration;
+ }
+
+ public IConfiguration Configuration { get; }
+
+
+ // This method gets called by the runtime. Use this method to add services to the container.
+ // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
+ public void ConfigureServices(IServiceCollection services)
+ {
+ services.AddSignalR(hubOptions =>
+ {
+ hubOptions.MaximumReceiveMessageSize = long.MaxValue;
+ });
+ services.AddSingleton();
+
+ services.AddResponseCompression(opts =>
+ {
+ opts.MimeTypes = ResponseCompressionDefaults.MimeTypes.Concat(new[] { "application/octet-stream" });
+ });
+ services.AddDbContext(options =>
+ options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
+ services.AddDatabaseDeveloperPageExceptionFilter();
+ services.AddAuthentication(options => options.DefaultScheme = SecretKeyAuthenticationHandler.AUTH_SCHEME)
+ .AddScheme(SecretKeyAuthenticationHandler.AUTH_SCHEME, options => { });
+ }
+
+ // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
+ public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
+ {
+ if (env.IsDevelopment())
+ {
+ app.UseDeveloperExceptionPage();
+ app.UseMigrationsEndPoint();
+ }
+ else
+ {
+ app.UseExceptionHandler("/Error");
+ // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
+ app.UseHsts();
+ }
+
+ app.UseHttpsRedirection();
+ app.UseStaticFiles();
+
+ app.UseRouting();
+
+ app.UseAuthentication();
+ app.UseAuthorization();
+
+ app.UseEndpoints(endpoints =>
+ {
+ endpoints.MapHub("/heartbeat");
+ endpoints.MapHub("/user");
+ endpoints.MapHub("/files", options =>
+ {
+ options.ApplicationMaxBufferSize = long.MaxValue;
+ options.TransportMaxBufferSize = long.MaxValue;
+ });
+ });
+ }
+ }
+}
diff --git a/MareSynchronosServer/MareSynchronosServer/appsettings.Development.json b/MareSynchronosServer/MareSynchronosServer/appsettings.Development.json
new file mode 100644
index 0000000..5173757
--- /dev/null
+++ b/MareSynchronosServer/MareSynchronosServer/appsettings.Development.json
@@ -0,0 +1,10 @@
+{
+ "DetailedErrors": true,
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft": "Warning",
+ "Microsoft.Hosting.Lifetime": "Information"
+ }
+ }
+}
diff --git a/MareSynchronosServer/MareSynchronosServer/appsettings.json b/MareSynchronosServer/MareSynchronosServer/appsettings.json
new file mode 100644
index 0000000..711b9b4
--- /dev/null
+++ b/MareSynchronosServer/MareSynchronosServer/appsettings.json
@@ -0,0 +1,13 @@
+{
+ "ConnectionStrings": {
+ "DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=aspnet-MareSynchronosServer-BA82A12A-0B30-463C-801D-B7E81318CD50;Trusted_Connection=True;MultipleActiveResultSets=true"
+ },
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft": "Warning",
+ "Microsoft.Hosting.Lifetime": "Information"
+ }
+ },
+ "AllowedHosts": "*"
+}