diff --git a/PluralKit.Web/Pages/Error.cshtml b/PluralKit.Web/Pages/Error.cshtml
new file mode 100644
index 00000000..6f92b956
--- /dev/null
+++ b/PluralKit.Web/Pages/Error.cshtml
@@ -0,0 +1,26 @@
+@page
+@model ErrorModel
+@{
+ ViewData["Title"] = "Error";
+}
+
+
Error.
+An error occurred while processing your request.
+
+@if (Model.ShowRequestId)
+{
+
+ Request ID: @Model.RequestId
+
+}
+
+Development Mode
+
+ Swapping to the Development environment displays detailed information about the error that occurred.
+
+
+ The Development environment shouldn't be enabled for deployed applications.
+ It can result in displaying sensitive information from exceptions to end users.
+ For local debugging, enable the Development environment by setting the ASPNETCORE_ENVIRONMENT environment variable to Development
+ and restarting the app.
+
diff --git a/PluralKit.Web/Pages/Error.cshtml.cs b/PluralKit.Web/Pages/Error.cshtml.cs
new file mode 100644
index 00000000..efaccc32
--- /dev/null
+++ b/PluralKit.Web/Pages/Error.cshtml.cs
@@ -0,0 +1,23 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.RazorPages;
+
+namespace PluralKit.Web.Pages
+{
+ [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
+ public class ErrorModel : PageModel
+ {
+ public string RequestId { get; set; }
+
+ public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
+
+ public void OnGet()
+ {
+ RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier;
+ }
+ }
+}
\ No newline at end of file
diff --git a/PluralKit.Web/Pages/Index.cshtml b/PluralKit.Web/Pages/Index.cshtml
new file mode 100644
index 00000000..b5f0c15f
--- /dev/null
+++ b/PluralKit.Web/Pages/Index.cshtml
@@ -0,0 +1,10 @@
+@page
+@model IndexModel
+@{
+ ViewData["Title"] = "Home page";
+}
+
+
diff --git a/PluralKit.Web/Pages/Index.cshtml.cs b/PluralKit.Web/Pages/Index.cshtml.cs
new file mode 100644
index 00000000..00158342
--- /dev/null
+++ b/PluralKit.Web/Pages/Index.cshtml.cs
@@ -0,0 +1,16 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.RazorPages;
+
+namespace PluralKit.Web.Pages
+{
+ public class IndexModel : PageModel
+ {
+ public void OnGet()
+ {
+ }
+ }
+}
\ No newline at end of file
diff --git a/PluralKit.Web/Pages/Shared/_Layout.cshtml b/PluralKit.Web/Pages/Shared/_Layout.cshtml
new file mode 100644
index 00000000..5f0d0a73
--- /dev/null
+++ b/PluralKit.Web/Pages/Shared/_Layout.cshtml
@@ -0,0 +1,38 @@
+
+
+
+
+
+ @ViewData["Title"] - PluralKit.Web
+
+
+
+
+
+
+
+
+
+
+ @RenderBody()
+
+
+
+
+
+@RenderSection("Scripts", required: false)
+
+
+
+
+
+
\ No newline at end of file
diff --git a/PluralKit.Web/Pages/ViewSystem.cshtml b/PluralKit.Web/Pages/ViewSystem.cshtml
new file mode 100644
index 00000000..7b96e6ce
--- /dev/null
+++ b/PluralKit.Web/Pages/ViewSystem.cshtml
@@ -0,0 +1,122 @@
+@page "/s/{systemId}"
+@model PluralKit.Web.Pages.ViewSystem
+
+
+
+
+
+ @Model.System.Hid
+
+
+ @if (Model.System.Tag != null)
+ {
+
+
+ @Model.System.Tag
+
+ }
+
+ @if (Model.System.UiTz != null)
+ {
+
+
+ @Model.System.UiTz
+
+ }
+
+
+ @if (Model.System.Name != null)
+ {
+
@Model.System.Name
+ }
+
+ @if (Model.System.Description != null)
+ {
+
@Model.System.Description
+ }
+
+
Members
+ @foreach (var member in Model.Members)
+ {
+
+
+
+
@member.Name
+
@member.Description
+
+
+
+
+ @member.Hid
+
+
+ @if (member.Birthday != null)
+ {
+
+
+ @member.BirthdayString
+
+ }
+
+ @if (member.Pronouns != null)
+ {
+
+
+ @member.Pronouns
+
+ }
+
+
+
+ }
+
+
+@section Scripts {
+
+}
\ No newline at end of file
diff --git a/PluralKit.Web/Pages/ViewSystem.cshtml.cs b/PluralKit.Web/Pages/ViewSystem.cshtml.cs
new file mode 100644
index 00000000..b231e7f4
--- /dev/null
+++ b/PluralKit.Web/Pages/ViewSystem.cshtml.cs
@@ -0,0 +1,32 @@
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.RazorPages;
+
+namespace PluralKit.Web.Pages
+{
+ public class ViewSystem : PageModel
+ {
+ private SystemStore _systems;
+ private MemberStore _members;
+
+ public ViewSystem(SystemStore systems, MemberStore members)
+ {
+ _systems = systems;
+ _members = members;
+ }
+
+ public PKSystem System { get; set; }
+ public IEnumerable Members { get; set; }
+
+ public async Task OnGet(string systemId)
+ {
+ System = await _systems.GetByHid(systemId);
+ if (System == null) return NotFound();
+
+ Members = await _members.GetBySystem(System);
+
+ return Page();
+ }
+ }
+}
\ No newline at end of file
diff --git a/PluralKit.Web/Pages/_ViewImports.cshtml b/PluralKit.Web/Pages/_ViewImports.cshtml
new file mode 100644
index 00000000..82dd3ee0
--- /dev/null
+++ b/PluralKit.Web/Pages/_ViewImports.cshtml
@@ -0,0 +1,3 @@
+@using PluralKit.Web
+@namespace PluralKit.Web.Pages
+@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
diff --git a/PluralKit.Web/Pages/_ViewStart.cshtml b/PluralKit.Web/Pages/_ViewStart.cshtml
new file mode 100644
index 00000000..24f33e00
--- /dev/null
+++ b/PluralKit.Web/Pages/_ViewStart.cshtml
@@ -0,0 +1,3 @@
+@{
+ Layout = "Shared/_Layout";
+}
\ No newline at end of file
diff --git a/PluralKit.Web/PluralKit.Web.csproj b/PluralKit.Web/PluralKit.Web.csproj
new file mode 100644
index 00000000..74760155
--- /dev/null
+++ b/PluralKit.Web/PluralKit.Web.csproj
@@ -0,0 +1,21 @@
+
+
+
+ netcoreapp2.2
+ full
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/PluralKit.Web/Program.cs b/PluralKit.Web/Program.cs
new file mode 100644
index 00000000..82af97b6
--- /dev/null
+++ b/PluralKit.Web/Program.cs
@@ -0,0 +1,19 @@
+using Microsoft.AspNetCore;
+using Microsoft.AspNetCore.Hosting;
+
+namespace PluralKit.Web
+{
+ public class Program
+ {
+ public static void Main(string[] args)
+ {
+ CreateWebHostBuilder(args).Build().Run();
+ }
+
+ public static IWebHostBuilder CreateWebHostBuilder(string[] args)
+ {
+ return WebHost.CreateDefaultBuilder(args)
+ .UseStartup();
+ }
+ }
+}
\ No newline at end of file
diff --git a/PluralKit.Web/Startup.cs b/PluralKit.Web/Startup.cs
new file mode 100644
index 00000000..3b201fd6
--- /dev/null
+++ b/PluralKit.Web/Startup.cs
@@ -0,0 +1,55 @@
+using System.Data;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
+using Npgsql;
+
+namespace PluralKit.Web
+{
+ 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.
+ public void ConfigureServices(IServiceCollection services)
+ {
+ var config = Configuration.GetSection("PluralKit").Get();
+
+ services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
+
+ services
+ .AddSingleton(_ => new NpgsqlConnection(config.Database))
+ .AddSingleton()
+ .AddSingleton()
+ .AddSingleton(config);
+ }
+
+ // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
+ public async void Configure(IApplicationBuilder app, IHostingEnvironment env)
+ {
+ if (env.IsDevelopment())
+ {
+ app.UseDeveloperExceptionPage();
+ }
+ else
+ {
+ app.UseExceptionHandler("/Error");
+ }
+
+ //app.UseHttpsRedirection();
+ app.UseMvc();
+
+ var conn = app.ApplicationServices.GetRequiredService();
+ conn.Open();
+
+ await Schema.CreateTables(conn);
+ }
+ }
+}
\ No newline at end of file
diff --git a/PluralKit.sln b/PluralKit.sln
index afa744d5..18256e5c 100644
--- a/PluralKit.sln
+++ b/PluralKit.sln
@@ -4,6 +4,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PluralKit.Bot", "PluralKit.
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PluralKit.Core", "PluralKit.Core\PluralKit.Core.csproj", "{5DBE037D-179D-4C05-8A28-35E37129C961}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PluralKit.Web", "PluralKit.Web\PluralKit.Web.csproj", "{975F9DED-78D1-4742-8412-DF70BB381E92}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -18,5 +20,9 @@ Global
{5DBE037D-179D-4C05-8A28-35E37129C961}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5DBE037D-179D-4C05-8A28-35E37129C961}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5DBE037D-179D-4C05-8A28-35E37129C961}.Release|Any CPU.Build.0 = Release|Any CPU
+ {975F9DED-78D1-4742-8412-DF70BB381E92}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {975F9DED-78D1-4742-8412-DF70BB381E92}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {975F9DED-78D1-4742-8412-DF70BB381E92}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {975F9DED-78D1-4742-8412-DF70BB381E92}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal