Compare commits
9 Commits
869ee77446
...
9ea4425c26
Author | SHA1 | Date | |
---|---|---|---|
9ea4425c26 | |||
9698a0b567 | |||
d7bc6a368c | |||
c62194ee70 | |||
1862fa3ab4 | |||
9dc12ba3b3 | |||
90f6d34af9 | |||
a7f7d7862c | |||
d9101034c3 |
112
OliverBooth.Api/Controllers/v1/BadgeController.cs
Normal file
112
OliverBooth.Api/Controllers/v1/BadgeController.cs
Normal file
@ -0,0 +1,112 @@
|
||||
using System.Net.Http.Headers;
|
||||
using System.Reflection;
|
||||
using System.Text.Json.Serialization;
|
||||
using Asp.Versioning;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace OliverBooth.Api.Controllers.v1;
|
||||
|
||||
[ApiController]
|
||||
[Route("v{version:apiVersion}/api/badge")]
|
||||
[Produces("application/json")]
|
||||
[ApiVersion(1)]
|
||||
public sealed class BadgeController : ControllerBase
|
||||
{
|
||||
private readonly IConfiguration _configuration;
|
||||
private readonly IHttpClientFactory _httpClientFactory;
|
||||
private readonly string _version;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="BadgeController" /> class.
|
||||
/// </summary>
|
||||
/// <param name="configuration">The configuration.</param>
|
||||
/// <param name="httpClientFactory">The HTTP client factory.</param>
|
||||
public BadgeController(IConfiguration configuration, IHttpClientFactory httpClientFactory)
|
||||
{
|
||||
_configuration = configuration;
|
||||
_httpClientFactory = httpClientFactory;
|
||||
|
||||
var attribute = typeof(Program).Assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>();
|
||||
_version = attribute?.InformationalVersion ?? "1.0.0";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a JSON object that is compatible with a shields.io custom endpoint.
|
||||
/// </summary>
|
||||
/// <param name="repo">The repository name.</param>
|
||||
/// <param name="workflow">The workflow.</param>
|
||||
/// <param name="owner">The owner. Defaults to <c>oliverbooth</c>.</param>
|
||||
/// <returns>A JSON object.</returns>
|
||||
[HttpGet("status/{repo}/{workflow}")]
|
||||
[HttpGet("status/{owner}/{repo}/{workflow}")]
|
||||
[EndpointDescription("Returns a JSON object that is compatible with a shields.io custom endpoint.")]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
public async Task<IActionResult> StatusAsync(string repo, string workflow, string owner = "oliverbooth")
|
||||
{
|
||||
string? githubToken = _configuration.GetSection("GitHub:Token").Value;
|
||||
|
||||
var url = $"https://api.github.com/repos/{owner}/{repo}/actions/workflows/{workflow}/runs";
|
||||
Console.WriteLine(url);
|
||||
using HttpClient client = _httpClientFactory.CreateClient();
|
||||
using var request = new HttpRequestMessage();
|
||||
request.RequestUri = new Uri(url);
|
||||
request.Headers.Add("Accept", "application/json");
|
||||
request.Headers.Add("Authorization", $"Bearer {githubToken}");
|
||||
request.Headers.Add("X-GitHub-Api-Version", "2022-11-28");
|
||||
request.Headers.UserAgent.Add(new ProductInfoHeaderValue("oliverbooth.dev", _version));
|
||||
|
||||
using HttpResponseMessage response = await client.SendAsync(request);
|
||||
Console.WriteLine(await response.Content.ReadAsStringAsync());
|
||||
var body = await response.Content.ReadFromJsonAsync<WorkflowRunSchema>();
|
||||
WorkflowRun? run = body?.WorkflowRuns.FirstOrDefault(r => r.Status == WorkflowRunStatus.Completed);
|
||||
if (run is not null)
|
||||
{
|
||||
var color = run.Conclusion switch
|
||||
{
|
||||
WorkflowRunConclusion.Failure => "e05d44",
|
||||
WorkflowRunConclusion.Success => "44cc11",
|
||||
_ => "lightgray"
|
||||
};
|
||||
var message = run.Conclusion switch
|
||||
{
|
||||
WorkflowRunConclusion.Failure => "failing",
|
||||
WorkflowRunConclusion.Success => "passing",
|
||||
_ => "unknown"
|
||||
};
|
||||
|
||||
return Ok(new { schemaVersion = 1, label = "build", color, message });
|
||||
}
|
||||
|
||||
return Ok(new { schemaVersion = 1, label = "build", color = "lightgray", message = "unknown" });
|
||||
}
|
||||
|
||||
private class WorkflowRunSchema
|
||||
{
|
||||
[JsonPropertyName("workflow_runs"), JsonInclude]
|
||||
public WorkflowRun[] WorkflowRuns { get; set; } = Array.Empty<WorkflowRun>();
|
||||
}
|
||||
|
||||
private class WorkflowRun
|
||||
{
|
||||
[JsonPropertyName("conclusion"), JsonInclude]
|
||||
[JsonConverter(typeof(JsonStringEnumConverter<WorkflowRunConclusion>))]
|
||||
public WorkflowRunConclusion Conclusion { get; set; } = WorkflowRunConclusion.Unknown;
|
||||
|
||||
[JsonPropertyName("status"), JsonInclude]
|
||||
[JsonConverter(typeof(JsonStringEnumConverter<WorkflowRunStatus>))]
|
||||
public WorkflowRunStatus Status { get; set; } = WorkflowRunStatus.Unknown;
|
||||
}
|
||||
|
||||
private enum WorkflowRunStatus
|
||||
{
|
||||
Unknown = -1,
|
||||
Completed
|
||||
}
|
||||
|
||||
private enum WorkflowRunConclusion
|
||||
{
|
||||
Unknown = -1,
|
||||
Success,
|
||||
Failure
|
||||
}
|
||||
}
|
112
OliverBooth.Api/Controllers/v2/BadgeController.cs
Normal file
112
OliverBooth.Api/Controllers/v2/BadgeController.cs
Normal file
@ -0,0 +1,112 @@
|
||||
using System.Net.Http.Headers;
|
||||
using System.Reflection;
|
||||
using System.Text.Json.Serialization;
|
||||
using Asp.Versioning;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace OliverBooth.Api.Controllers.v2;
|
||||
|
||||
[ApiController]
|
||||
[Route("v{version:apiVersion}/api/badge")]
|
||||
[Produces("application/json")]
|
||||
[ApiVersion(2)]
|
||||
public sealed class BadgeController : ControllerBase
|
||||
{
|
||||
private readonly IConfiguration _configuration;
|
||||
private readonly IHttpClientFactory _httpClientFactory;
|
||||
private readonly string _version;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="BadgeController" /> class.
|
||||
/// </summary>
|
||||
/// <param name="configuration">The configuration.</param>
|
||||
/// <param name="httpClientFactory">The HTTP client factory.</param>
|
||||
public BadgeController(IConfiguration configuration, IHttpClientFactory httpClientFactory)
|
||||
{
|
||||
_configuration = configuration;
|
||||
_httpClientFactory = httpClientFactory;
|
||||
|
||||
var attribute = typeof(Program).Assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>();
|
||||
_version = attribute?.InformationalVersion ?? "1.0.0";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a JSON object that is compatible with a shields.io custom endpoint.
|
||||
/// </summary>
|
||||
/// <param name="repo">The repository name.</param>
|
||||
/// <param name="workflow">The workflow.</param>
|
||||
/// <param name="owner">The owner. Defaults to <c>oliverbooth</c>.</param>
|
||||
/// <returns>A JSON object.</returns>
|
||||
[HttpGet("status/{repo}/{workflow}")]
|
||||
[HttpGet("status/{owner}/{repo}/{workflow}")]
|
||||
[EndpointDescription("Returns a JSON object that is compatible with a shields.io custom endpoint.")]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
public async Task<IActionResult> StatusAsync(string repo, string workflow, string owner = "oliverbooth")
|
||||
{
|
||||
string? githubToken = _configuration.GetSection("GitHub:Token").Value;
|
||||
|
||||
var url = $"https://api.github.com/repos/{owner}/{repo}/actions/workflows/{workflow}/runs";
|
||||
Console.WriteLine(url);
|
||||
using HttpClient client = _httpClientFactory.CreateClient();
|
||||
using var request = new HttpRequestMessage();
|
||||
request.RequestUri = new Uri(url);
|
||||
request.Headers.Add("Accept", "application/json");
|
||||
request.Headers.Add("Authorization", $"Bearer {githubToken}");
|
||||
request.Headers.Add("X-GitHub-Api-Version", "2022-11-28");
|
||||
request.Headers.UserAgent.Add(new ProductInfoHeaderValue("oliverbooth.dev", _version));
|
||||
|
||||
using HttpResponseMessage response = await client.SendAsync(request);
|
||||
Console.WriteLine(await response.Content.ReadAsStringAsync());
|
||||
var body = await response.Content.ReadFromJsonAsync<WorkflowRunSchema>();
|
||||
WorkflowRun? run = body?.WorkflowRuns.FirstOrDefault(r => r.Status == WorkflowRunStatus.Completed);
|
||||
if (run is not null)
|
||||
{
|
||||
var color = run.Conclusion switch
|
||||
{
|
||||
WorkflowRunConclusion.Failure => "e05d44",
|
||||
WorkflowRunConclusion.Success => "44cc11",
|
||||
_ => "lightgray"
|
||||
};
|
||||
var message = run.Conclusion switch
|
||||
{
|
||||
WorkflowRunConclusion.Failure => "failing",
|
||||
WorkflowRunConclusion.Success => "passing",
|
||||
_ => "unknown"
|
||||
};
|
||||
|
||||
return Ok(new { schemaVersion = 1, label = "build", color, message });
|
||||
}
|
||||
|
||||
return Ok(new { schemaVersion = 1, label = "build", color = "lightgray", message = "unknown" });
|
||||
}
|
||||
|
||||
private class WorkflowRunSchema
|
||||
{
|
||||
[JsonPropertyName("workflow_runs"), JsonInclude]
|
||||
public WorkflowRun[] WorkflowRuns { get; set; } = Array.Empty<WorkflowRun>();
|
||||
}
|
||||
|
||||
private class WorkflowRun
|
||||
{
|
||||
[JsonPropertyName("conclusion"), JsonInclude]
|
||||
[JsonConverter(typeof(JsonStringEnumConverter<WorkflowRunConclusion>))]
|
||||
public WorkflowRunConclusion Conclusion { get; set; } = WorkflowRunConclusion.Unknown;
|
||||
|
||||
[JsonPropertyName("status"), JsonInclude]
|
||||
[JsonConverter(typeof(JsonStringEnumConverter<WorkflowRunStatus>))]
|
||||
public WorkflowRunStatus Status { get; set; } = WorkflowRunStatus.Unknown;
|
||||
}
|
||||
|
||||
private enum WorkflowRunStatus
|
||||
{
|
||||
Unknown = -1,
|
||||
Completed
|
||||
}
|
||||
|
||||
private enum WorkflowRunConclusion
|
||||
{
|
||||
Unknown = -1,
|
||||
Success,
|
||||
Failure
|
||||
}
|
||||
}
|
@ -11,11 +11,11 @@
|
||||
<PackageReference Include="Asp.Versioning.Mvc" Version="8.0.0"/>
|
||||
<PackageReference Include="BCrypt.Net-Core" Version="1.6.0"/>
|
||||
<PackageReference Include="Humanizer.Core" Version="2.14.1"/>
|
||||
<PackageReference Include="Markdig" Version="0.35.0"/>
|
||||
<PackageReference Include="Markdig" Version="0.36.2"/>
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="8.0.0"/>
|
||||
<PackageReference Include="NetBarcode" Version="1.7.0"/>
|
||||
<PackageReference Include="Otp.NET" Version="1.3.0"/>
|
||||
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="8.0.0"/>
|
||||
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="8.0.2"/>
|
||||
<PackageReference Include="Serilog" Version="3.1.1"/>
|
||||
<PackageReference Include="Serilog.AspNetCore" Version="8.0.1"/>
|
||||
<PackageReference Include="Serilog.Extensions.Logging" Version="8.0.0"/>
|
||||
|
@ -31,11 +31,11 @@
|
||||
<PackageReference Include="FluentFTP" Version="49.0.2"/>
|
||||
<PackageReference Include="FluentFTP.Logging" Version="1.0.0"/>
|
||||
<PackageReference Include="HtmlAgilityPack" Version="1.11.59"/>
|
||||
<PackageReference Include="MailKit" Version="4.3.0"/>
|
||||
<PackageReference Include="MailKit" Version="4.4.0"/>
|
||||
<PackageReference Include="MailKitSimplified.Sender" Version="2.9.0"/>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.Web" Version="8.0.2"/>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" Version="8.0.2"/>
|
||||
<PackageReference Include="Microsoft.Extensions.FileProviders.Embedded" Version="8.0.2"/>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.Web" Version="8.0.3"/>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" Version="8.0.3"/>
|
||||
<PackageReference Include="Microsoft.Extensions.FileProviders.Embedded" Version="8.0.3"/>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -118,20 +118,6 @@
|
||||
|
||||
<div style="margin:50px 0;"></div>
|
||||
|
||||
@if (!doAprilFools && DateTimeOffset.UtcNow < new DateTime(2024, 03, 08))
|
||||
{
|
||||
<div id="usa-countdown" class="container">
|
||||
<div class="row">
|
||||
<div class="col-3 usa-countdown-element" id="usa-countdown-days">00</div>
|
||||
<div class="col-3 usa-countdown-element" id="usa-countdown-hours">00</div>
|
||||
<div class="col-3 usa-countdown-element" id="usa-countdown-minutes">00</div>
|
||||
<div class="col-3 usa-countdown-element" id="usa-countdown-seconds">00</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
<div style="margin:50px 0;"></div>
|
||||
|
||||
<div class="container">
|
||||
@if (doAprilFools)
|
||||
{
|
||||
|
@ -26,6 +26,7 @@ body {
|
||||
main.container {
|
||||
background: #333;
|
||||
padding: 20px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
a {
|
||||
@ -109,7 +110,7 @@ nav {
|
||||
ul.site-nav {
|
||||
list-style-type: none;
|
||||
padding: 0;
|
||||
margin: 0 auto;
|
||||
margin: 20px auto 0 auto;
|
||||
|
||||
li {
|
||||
display: inline-block;
|
||||
@ -120,7 +121,7 @@ nav {
|
||||
}
|
||||
|
||||
a {
|
||||
font-size: 24px;
|
||||
font-size: 20px;
|
||||
|
||||
&:link, &:visited, &:hover, &:active {
|
||||
text-decoration: none;
|
||||
@ -142,6 +143,7 @@ nav {
|
||||
article {
|
||||
background: #333333;
|
||||
padding: 20px;
|
||||
border-radius: 5px;
|
||||
|
||||
*:last-child {
|
||||
margin-bottom: 0;
|
||||
|
@ -82,33 +82,6 @@ class UI {
|
||||
UI.updateProjectCards(element);
|
||||
}
|
||||
|
||||
public static updateUsaCountdown(element?: Element){
|
||||
element = element || document.getElementById("usa-countdown");
|
||||
|
||||
const daysElement = element.querySelector("#usa-countdown-days");
|
||||
const hoursElement = element.querySelector("#usa-countdown-hours");
|
||||
const minutesElement = element.querySelector("#usa-countdown-minutes");
|
||||
const secondsElement = element.querySelector("#usa-countdown-seconds");
|
||||
|
||||
const start = new Date().getTime();
|
||||
const end = Date.UTC(2024, 2, 7, 13, 20);
|
||||
const diff = end - start;
|
||||
let days = Math.floor(diff / (1000 * 60 * 60 * 24));
|
||||
let hours = Math.floor((diff % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
|
||||
let minutes = Math.floor((diff % (1000 * 60 * 60)) / (1000 * 60));
|
||||
let seconds = Math.floor((diff % (1000 * 60)) / 1000);
|
||||
|
||||
if (days < 0) days = 0
|
||||
if (hours < 0) hours = 0;
|
||||
if (minutes < 0) minutes = 0;
|
||||
if (seconds < 0) seconds = 0;
|
||||
|
||||
daysElement.innerHTML = days.toString().padStart(2, '0');
|
||||
hoursElement.innerHTML = hours.toString().padStart(2, '0');
|
||||
minutesElement.innerHTML = minutes.toString().padStart(2, '0');
|
||||
secondsElement.innerHTML = seconds.toString().padStart(2, '0');
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds Bootstrap tooltips to all elements with a title attribute.
|
||||
* @param element The element to search for elements with a title attribute in.
|
||||
|
@ -98,13 +98,6 @@ declare const Prism: any;
|
||||
|
||||
UI.updateUI();
|
||||
|
||||
const usaCountdown = document.getElementById("usa-countdown");
|
||||
if (usaCountdown) {
|
||||
usaCountdown.addEventListener("click", () => window.location.href = "/blog/2024/02/19/the-american");
|
||||
UI.updateUsaCountdown(usaCountdown);
|
||||
setInterval(() => UI.updateUsaCountdown(usaCountdown), 1000);
|
||||
}
|
||||
|
||||
let avatarType = 0;
|
||||
const headshot = document.getElementById("index-headshot") as HTMLImageElement;
|
||||
if (headshot) {
|
||||
|
Loading…
Reference in New Issue
Block a user