14 Commits

Author SHA1 Message Date
tparnell
a3838b0f4d picture elem 2019-02-03 20:25:08 -05:00
tparnell
38f82061e9 do not block anything 2019-02-03 20:22:21 -05:00
tparnell
57a8bba66a add csp back 2019-02-03 19:18:11 -05:00
tparnell
43d6e33638 rm csp 2019-02-03 19:11:28 -05:00
tparnell
d875ca6fea headers 🎉 2019-02-03 18:53:16 -05:00
Tommy Parnell
d846a538a0 headerz 2019-02-03 17:33:01 -05:00
Tommy Parnell
00b711aef4 increase hsts header 2019-02-03 13:14:20 -05:00
Tommy Parnell
dbb6ae208b output cache all the things 2019-02-03 13:01:21 -05:00
Tommy Parnell
de62e6275d inline styles 2019-02-03 11:53:52 -05:00
tparnell
d873be97d8 bettah config 2019-02-02 14:17:20 -05:00
tparnell
f3080faae0 start to move things to config 2019-02-02 13:49:46 -05:00
tparnell
6ed0ef4205 only render gtm in production 2019-02-02 13:30:30 -05:00
tparnell
ab9250b968 Revert "fix menu animation"
This reverts commit c24684fa8b.
2019-02-02 12:38:01 -05:00
tparnell
c24684fa8b fix menu animation 2019-02-02 12:28:55 -05:00
15 changed files with 193 additions and 43 deletions

17
.vscode/launch.json vendored
View File

@@ -1,18 +1,17 @@
{
// Use IntelliSense to find out which attributes exist for C# debugging
// Use hover for the description of the existing attributes
// For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md
"version": "0.2.0",
"configurations": [
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": ".NET Core Launch (web)",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build",
// If you have changed target frameworks, make sure to update the program path.
"program": "${workspaceFolder}/TerribleDev.Blog.Web/bin/Debug/netcoreapp2.2/TerribleDev.Blog.Web.dll",
"program": "${workspaceFolder}/src/TerribleDev.Blog.Web/bin/Debug/netcoreapp2.2/TerribleDev.Blog.Web.dll",
"args": [],
"cwd": "${workspaceFolder}/TerribleDev.Blog.Web",
"cwd": "${workspaceFolder}/src/TerribleDev.Blog.Web",
"stopAtEntry": false,
"internalConsoleOptions": "openOnSessionStart",
"launchBrowser": {
@@ -42,5 +41,5 @@
"request": "attach",
"processId": "${command:pickProcess}"
}
,]
]
}

2
.vscode/tasks.json vendored
View File

@@ -7,7 +7,7 @@
"type": "process",
"args": [
"build",
"${workspaceFolder}/TerribleDev.Blog.Web/TerribleDev.Blog.Web.csproj"
"${workspaceFolder}/src/TerribleDev.Blog.Web/TerribleDev.Blog.Web.csproj"
],
"problemMatcher": "$msCompile"
}

7
docker-compose.yml Normal file
View File

@@ -0,0 +1,7 @@
version: '3'
services:
webapp:
build: ./src/TerribleDev.Blog.Web
ports:
- "80:80"
- "443:443"

View File

@@ -47,6 +47,7 @@ namespace TerribleDev.Blog.Web.Controllers
[Route("/")]
[Route("/index.html")]
[Route("/page/{pageNumber?}" )]
[OutputCache(Duration = 31536000, VaryByParam = "pageNumber")]
public IActionResult Index(int pageNumber = 1)
{
if(!postsByPage.TryGetValue(pageNumber, out var result))

View File

@@ -15,12 +15,18 @@ namespace TerribleDev.Blog.Web.Controllers
{
public class SeoController : Controller
{
private readonly BlogConfiguration configuration;
public SeoController(BlogConfiguration configuration)
{
this.configuration = configuration;
}
public static DateTimeOffset publishDate = DateTimeOffset.UtcNow; // keep publish date in memory so we just return when the server was kicked
public static IEnumerable<SyndicationItem> postsToSyndication = HomeController.postsAsList.Select(a => a.ToSyndicationItem()).ToList();
[Route("/rss")]
[Route("/rss.xml")]
[ResponseCache(Duration = 7200)]
[OutputCache(Duration = 86400)]
[OutputCache(Duration = 31536000)]
public async Task Rss()
{
Response.StatusCode = 200;
@@ -28,8 +34,8 @@ namespace TerribleDev.Blog.Web.Controllers
using (XmlWriter xmlWriter = XmlWriter.Create(this.Response.Body, new XmlWriterSettings() { Async = true, Indent = false, Encoding = Encoding.UTF8 }))
{
var writer = new RssFeedWriter(xmlWriter);
await writer.WriteTitle("The Ramblings of TerribleDev");
await writer.WriteValue("link", "https://blog.terribledev.io");
await writer.WriteTitle(configuration.Title);
await writer.WriteValue("link", configuration.Link);
await writer.WriteDescription("My name is Tommy Parnell. I usually go by TerribleDev on the internets. These are just some of my writings and rants about the software space.");
foreach (var item in postsToSyndication)
@@ -43,12 +49,12 @@ namespace TerribleDev.Blog.Web.Controllers
}
[Route("/sitemap.xml")]
[ResponseCache(Duration = 7200)]
[OutputCache(Duration = 86400)]
[OutputCache(Duration = 31536000)]
public void SiteMap()
{
Response.StatusCode = 200;
Response.ContentType = "text/xml";
var sitewideLinks = new List<SiteMapItem>(HomeController.tagToPost.Keys.Select(a=> new SiteMapItem() { LastModified = DateTime.UtcNow, Location = $"https://blog.terribledev.io/tag/{a}/"}))
var sitewideLinks = new List<SiteMapItem>(HomeController.tagToPost.Keys.Select(a => new SiteMapItem() { LastModified = DateTime.UtcNow, Location = $"https://blog.terribledev.io/tag/{a}/" }))
{
new SiteMapItem() { LastModified = DateTime.UtcNow, Location="https://blog.terribledev.io/all-tags/" }
};

View File

@@ -50,7 +50,7 @@ namespace TerribleDev.Blog.Web.MarkExtension
return false;
}
this.images.Add(linkInline.Url);
return false;
return true;
}
}
}

View File

@@ -0,0 +1,18 @@
using Markdig.Renderers.Html;
using Markdig.Syntax.Inlines;
namespace TerribleDev.Blog.Web.MarkExtension
{
public class PictureInlineRenderer : HtmlObjectRenderer<LinkInline>
{
protected override void Write(HtmlRenderer renderer, LinkInline link)
{
if(!link.IsImage)
{
base.Write(renderer, link);
}
}
}
}

View File

@@ -0,0 +1,8 @@
namespace TerribleDev.Blog.Web.Models
{
public class BlogConfiguration
{
public string Title { get; set; }
public string Link { get; set; }
}
}

View File

@@ -15,20 +15,33 @@ using Microsoft.Extensions.FileProviders;
using Microsoft.Net.Http.Headers;
using HardHat.Middlewares;
using HardHat;
using TerribleDev.Blog.Web.Models;
namespace TerribleDev.Blog.Web
{
public class Startup
{
public Startup(IConfiguration configuration)
public Startup(IConfiguration configuration, IHostingEnvironment env)
{
Configuration = configuration;
Env = env;
}
public IConfiguration Configuration { get; }
public IHostingEnvironment Env { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
Func<BlogConfiguration> getBlog = () => Configuration.GetSection("Blog").Get<BlogConfiguration>();
if (Env.IsDevelopment())
{
services.AddTransient(a => getBlog());
}
else
{
services.AddSingleton(getBlog());
}
services.AddResponseCompression(a =>
{
a.EnableForHttps = true;
@@ -57,31 +70,47 @@ namespace TerribleDev.Blog.Web
app.UseHttpsRedirection();
app.UseResponseCompression();
var cacheTime = env.IsDevelopment() ? 0 : 31536000;
app.UseStaticFiles(new StaticFileOptions
{
OnPrepareResponse = ctx =>
{
ctx.Context.Response.Headers[HeaderNames.CacheControl] =
"public,max-age=" + cacheTime;
}
OnPrepareResponse = ctx =>
{
ctx.Context.Response.Headers[HeaderNames.CacheControl] =
"public,max-age=" + cacheTime;
}
});
app.UseStaticFiles(new StaticFileOptions
{
FileProvider = new PhysicalFileProvider(Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "img")),
OnPrepareResponse = ctx =>
{
ctx.Context.Response.Headers[HeaderNames.CacheControl] =
"public,max-age=" + cacheTime;
}
OnPrepareResponse = ctx =>
{
ctx.Context.Response.Headers[HeaderNames.CacheControl] =
"public,max-age=" + cacheTime;
}
});
app.UseRewriter(new Microsoft.AspNetCore.Rewrite.RewriteOptions().AddRedirect("(.*[^/|.xml|.html])$", "$1/", 301));
app.UseIENoOpen();
app.UseNoMimeSniff();
app.UseCrossSiteScriptingFilters();
app.UseFrameGuard(new FrameGuardOptions(FrameGuardOptions.FrameGuard.SAMEORIGIN));
app.UseHsts(TimeSpan.FromDays(30), false, preload: true);
app.UseHsts(TimeSpan.FromDays(365), false, preload: true);
app.UseContentSecurityPolicy(
new ContentSecurityPolicy()
{
// DefaultSrc = new HashSet<string>() {
// CSPConstants.Self, "https://www.google-analytics.com", "https://www.googletagmanager.com", "https://stats.g.doubleclick.net"
// },
// ScriptSrc = new HashSet<string>()
// {
// CSPConstants.Self, CSPConstants.UnsafeInline, "https://www.google-analytics.com", "https://www.googletagmanager.com", "https://stats.g.doubleclick.net"
// },
// StyleSrc = new HashSet<string>()
// {
// CSPConstants.Self, CSPConstants.UnsafeInline
// },
UpgradeInsecureRequests = true
});
app.UseOutputCaching();
app.UseMvc();
}

View File

@@ -0,0 +1,72 @@
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Razor.TagHelpers;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.FileProviders;
using System.IO;
using System.Threading.Tasks;
namespace TerribleDev.Blog.Web.Taghelpers
{
[HtmlTargetElement("inline-style")]
public class InlineStyleTagHelper : TagHelper
{
[HtmlAttributeName("href")]
public string Href { get; set; }
private IHostingEnvironment HostingEnvironment { get; }
private IMemoryCache Cache { get; }
public InlineStyleTagHelper(IHostingEnvironment hostingEnvironment, IMemoryCache cache)
{
HostingEnvironment = hostingEnvironment;
Cache = cache;
}
public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
{
var path = Href;
// Get the value from the cache, or compute the value and add it to the cache
var fileContent = await Cache.GetOrCreateAsync("InlineStyleTagHelper-" + path, async entry =>
{
var fileProvider = HostingEnvironment.WebRootFileProvider;
if(HostingEnvironment.IsDevelopment())
{
var changeToken = fileProvider.Watch(path);
entry.AddExpirationToken(changeToken);
}
entry.SetPriority(CacheItemPriority.NeverRemove);
var file = fileProvider.GetFileInfo(path);
if (file == null || !file.Exists)
return null;
return await ReadFileContent(file);
});
if (fileContent == null)
{
output.SuppressOutput();
return;
}
output.TagName = "style";
output.Attributes.RemoveAll("href");
output.Content.AppendHtml(fileContent);
}
private static async Task<string> ReadFileContent(IFileInfo file)
{
using (var stream = file.CreateReadStream())
using (var textReader = new StreamReader(stream))
{
return await textReader.ReadToEndAsync();
}
}
}
}

View File

@@ -26,7 +26,7 @@
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.0.2105168" />
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="2.2.0" />
<PackageReference Include="YamlDotNet" Version="5.3.0" />
<PackageReference Include="HardHat" Version="2.0.0" />
<PackageReference Include="HardHat" Version="2.1.1" />
<PackageReference Include="Microsoft.SyndicationFeed.ReaderWriter" Version="1.0.2" />
<PackageReference Include="WebEssentials.AspNetCore.OutputCaching" Version="1.0.16" />
</ItemGroup>

View File

@@ -1,4 +1,5 @@
@model IPost
@inject BlogConfiguration config
@model IPost
@{
ViewData["Title"] = "Post";
ViewData["HideNav"] = true;
@@ -13,7 +14,7 @@
<meta property="og:type" content="blog">
<meta property="og:title" content="@Model.Title">
<meta property="og:url" content="https://blog.terribledev.io/@Model.Url/">
<meta property="og:site_name" content="The Ramblings of TerribleDev">
<meta property="og:site_name" content="@config.Title">
<meta property="og:description" content="@Model.SummaryPlainShort">
<meta property="og:updated_time" content="@Model.PublishDate.ToString("O")">
<meta name="twitter:card" content="summary">

View File

@@ -1,11 +1,12 @@
<meta name="description" content="My name is Tommy Parnell. I usually go by TerribleDev on the internets. These are just some of my writings and rants about the software space." />
@inject BlogConfiguration config
<meta name="description" content="My name is Tommy Parnell. I usually go by TerribleDev on the internets. These are just some of my writings and rants about the software space." />
<meta property="og:type" content="blog">
<meta property="og:title" content="The Ramblings of TerribleDev">
<meta property="og:title" content="@config.Title">
<meta property="og:url" content="https://blog.terribledev.io/">
<meta property="og:site_name" content="The Ramblings of TerribleDev">
<meta property="og:site_name" content="@config.Title">
<meta property="og:description" content="My name is Tommy Parnell. I usually go by TerribleDev on the internets. These are just some of my writings and rants about the software space.">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="The Ramblings of TerribleDev">
<meta name="twitter:title" content="@config.Title">
<meta name="twitter:description" content="My name is Tommy Parnell. I usually go by TerribleDev on the internets. These are just some of my writings and rants about the software space.">
<meta name="twitter:creator" content="@@TerribleDev">
<meta property="og:image" content="https://www.gravatar.com/avatar/333e3cea32cd17ff2007d131df336061?s=640" />

View File

@@ -1,20 +1,24 @@
<!DOCTYPE html>
@inject BlogConfiguration config
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<partial name="Gtm" />
<environment names="Production">
<partial name="Gtm" />
</environment>
<meta name="author" content="Tommy &quot;TerribleDev&quot; Parnell" />
<meta name="theme-color" content="#4A4A4A" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="alternate" type="application/atom+xml" async title="RSS" href="/rss.xml">
<link rel="manifest" href="~/manifest.json" async asp-append-version="true">
<link asp-append-version="true" rel="icon" async href="~/favicon.ico" />
<title>@ViewData["Title"] - The Ramblings of TerribleDev</title>
<title>@ViewData["Title"] - @config.Title</title>
<environment names="Development">
<link asp-append-version="true" rel="stylesheet" href="~/css/site.css" />
<inline-style href="css/site.css"></inline-style>
</environment>
<environment names="Production">
<link asp-append-version="true" rel="stylesheet" href="~/css/site.min.css" />
<inline-style href="css/site.min.css"></inline-style>
</environment>
@RenderSection("Head", false)
</head>
@@ -22,7 +26,7 @@
<partial name="Nav" />
<header class="header">
<svg aria-label="Open Menu" id="menuBtn" role="button" xmlns="http://www.w3.org/2000/svg" width="32" height="32"><path d="M4 10h24c1.104 0 2-.896 2-2s-.896-2-2-2H4c-1.104 0-2 .896-2 2s.896 2 2 2zm24 4H4c-1.104 0-2 .896-2 2s.896 2 2 2h24c1.104 0 2-.896 2-2s-.896-2-2-2zm0 8H4c-1.104 0-2 .896-2 2s.896 2 2 2h24c1.104 0 2-.896 2-2s-.896-2-2-2z" /></svg>
<div class="headerCallout"><a href="/" class="link-unstyled ">The Ramblings of TerribleDev</a></div>
<div class="headerCallout"><a href="/" class="link-unstyled ">@config.Title</a></div>
</header>
@{
var bodyBump = ViewData["HideNav"] == null ? "bodyWithNav": "";

View File

@@ -9,5 +9,9 @@
}
}
},
"AllowedHosts": "*"
"AllowedHosts": "*",
"Blog": {
"title": "The Ramblings of TerribleDev",
"link": "https://blog.terribledev.io"
}
}