-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmain.csx
203 lines (169 loc) · 6.49 KB
/
main.csx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
#!/usr/bin/env dotnet-script
#r "nuget: YamlDotNet, 8.1.1"
#r "nuget: Newtonsoft.Json, 12.0.3"
#r "nuget: morelinq, 3.3.2"
using YamlDotNet.Serialization;
using YamlDotNet.Serialization.NamingConventions;
using Newtonsoft.Json;
using System.Net.Http;
using MoreLinq;
if (Args.Count < 1 || Args.Count > 2) {
Console.WriteLine("Please provide the location of a Jekyll repository as an argument. Example: dotnet script main.csx -- path");
return;
}
var dryRun = Args.Count == 2;
if (dryRun) {
Console.WriteLine("Starting dry run");
}
Console.WriteLine("Finding cached URLs...");
var postPath = Path.GetFullPath(Path.Combine(Args[0], "_posts"));
var latestPostFrontMatter = Directory
.EnumerateFileSystemEntries(postPath, "*.md")
.Select(p => ParseFrontMatter(p))
.MaxBy(f => f.date)
.First();
const string ConfigFileName = "config.json";
var config = File.Exists(ConfigFileName)
? JsonConvert.DeserializeObject<Config>(File.ReadAllText(ConfigFileName))
: new Config("https://hjerpbakk.com");
var sitemapUrl = GetUrl("sitemap.xml");
var urls = new List<string>() {
config.BaseAddress,
GetUrl("feed.xml"),
sitemapUrl,
GetUrl("archives/"),
GetPostUrl(latestPostFrontMatter.Path)
};
urls.AddRange(latestPostFrontMatter.tags.Select(t => GetTagUrl(t)));
Console.WriteLine("Found the following URLs:");
Console.WriteLine(string.Join(Environment.NewLine, urls));
if (dryRun) {
return;
}
if (string.IsNullOrEmpty(config.CloudflareApiKey)
|| string.IsNullOrEmpty(config.CloudflareEmail)
|| string.IsNullOrEmpty(config.CloudflareZoneId)) {
Console.WriteLine("Add config.json if you want to automatically clear the Cloudflare cache");
return;
}
await ClearCloudflareCache();
await WarmupCache();
await SubmitSitemapToGoogle();
string GetUrl(string webPath) => config.BaseAddress + "/" + webPath;
string GetTagUrl(string tag) => GetUrl("tag/" + tag + "/");
string GetPostUrl(string latestPostPath) {
var postFileName = Path.GetFileNameWithoutExtension(latestPostPath);
var nameParts = postFileName.Split('-');
var year = nameParts[0];
var month = nameParts[1];
var day = nameParts[2];
var name = postFileName.Substring(11);
var postUrl = new StringBuilder("blog");
postUrl.Append('/');
postUrl.Append(year);
postUrl.Append('/');
postUrl.Append(month);
postUrl.Append('/');
postUrl.Append(day);
postUrl.Append('/');
postUrl.Append(name);
return GetUrl(postUrl.ToString());
}
FrontMatter ParseFrontMatter(string postPath) {
var frontMatterText = GetFrontMatterFromPost();
var deserializer = new DeserializerBuilder()
.WithNamingConvention(UnderscoredNamingConvention.Instance)
.IgnoreUnmatchedProperties()
.Build();
var frontMatter = deserializer.Deserialize<FrontMatter>(frontMatterText);
frontMatter.Path = postPath;
return frontMatter;
string GetFrontMatterFromPost() {
var fullText = File.ReadAllText(postPath);
var indexOfFirstLineBreak = fullText.IndexOf('\n');
var indexOfFrontMatterEnd = fullText.IndexOf("---\n", indexOfFirstLineBreak, StringComparison.InvariantCulture);
if (indexOfFrontMatterEnd == -1) {
throw new ArgumentException($"Could not parse front matter for {postPath}", nameof(postPath));
}
var frontMatterEnd = indexOfFrontMatterEnd + 3;
var frontMatterText = fullText.Substring(0, frontMatterEnd).Trim('-');
return frontMatterText;
}
}
async Task ClearCloudflareCache() {
Console.WriteLine("Clearing Cloudflare cache...");
var cloudflareContent = new CloudflareContent(urls);
var cloudflareContentString = JsonConvert.SerializeObject(cloudflareContent);
using (var httpClient = new HttpClient()) {
httpClient.BaseAddress = new Uri("http://api.cloudflare.com/");
var request = new HttpRequestMessage(HttpMethod.Delete, "client/v4/zones/" + config.CloudflareZoneId + "/purge_cache");
request.Content = new StringContent(cloudflareContentString, Encoding.UTF8, "application/json");
request.Headers.Add("X-Auth-Key", config.CloudflareApiKey);
request.Headers.Add("X-Auth-Email", config.CloudflareEmail);
var response = await httpClient.SendAsync(request);
var prettyPrintedResponse = FormatJson(await response.Content.ReadAsStringAsync());
Console.WriteLine(prettyPrintedResponse);
Console.WriteLine(response.IsSuccessStatusCode
? "Cloudflare cache cleared successfully"
: "Cloudflare request failed");
}
}
string FormatJson(string json) {
dynamic parsedJson = JsonConvert.DeserializeObject(json);
return JsonConvert.SerializeObject(parsedJson, Formatting.Indented);
}
async Task WarmupCache() {
Console.WriteLine("Warming cache...");
await Task.Delay(TimeSpan.FromSeconds(30));
foreach (var url in urls) {
await VerifyUrl(url);
}
Console.WriteLine("All URLs hit successfully");
}
async Task VerifyUrl(string url) {
try {
using (var httpClient = new HttpClient()) {
var request = new HttpRequestMessage(HttpMethod.Get, url);
var response = await httpClient.SendAsync(request);
response.EnsureSuccessStatusCode();
}
} catch (System.Exception) {
Console.WriteLine("Failed to hit " + url);
throw;
}
}
async Task SubmitSitemapToGoogle() {
Console.WriteLine("Submitting sitemap to Google...");
var googleUrl = "https://www.google.com/ping?sitemap=" + sitemapUrl;
await VerifyUrl(googleUrl);
}
struct FrontMatter {
public DateTime date { get; set; }
public List<string> tags { get; set; }
public string Path { get; set; }
}
readonly struct Config {
public Config(string baseAddress) {
BaseAddress = baseAddress;
CloudflareApiKey = null;
CloudflareEmail = null;
CloudflareZoneId = null;
}
[JsonConstructor]
public Config(string baseAddress, string cloudflareApiKey, string cloudflareEmail, string cloudflareZoneId) {
BaseAddress = baseAddress;
CloudflareApiKey = cloudflareApiKey;
CloudflareEmail = cloudflareEmail;
CloudflareZoneId = cloudflareZoneId;
}
public string BaseAddress { get; }
public string CloudflareApiKey { get; }
public string CloudflareEmail { get; }
public string CloudflareZoneId { get; }
}
readonly struct CloudflareContent {
public CloudflareContent(List<string> urls) {
files = urls;
}
public List<string> files { get; }
}