Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion .github/workflows/dependency-review.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ jobs:
# Fail the action if any vulnerabilities are found
fail-on-severity: moderate
# Allow licenses (add/remove as needed for your project)
allow-licenses: MIT, Apache-2.0, BSD-2-Clause, BSD-3-Clause, ISC, GPL-3.0
allow-licenses: MIT, Apache-2.0, BSD-2-Clause, BSD-3-Clause, ISC, GPL-3.0, MPL-1.1, MPL-2.0
# Allow dependencies even if license detection fails
# System.Net.Http and System.Text.RegularExpressions are Microsoft packages with MIT licenses
# but GitHub may flag them as "OTHER" due to license metadata format
allow-dependencies-licenses: pkg:nuget/System.Net.Http, pkg:nuget/System.Text.RegularExpressions
# Comment on PR with summary
comment-summary-in-pr: always
11 changes: 11 additions & 0 deletions Albatross.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,21 @@
<RemoveDir Directories="Generated" Condition="Exists('Generated')" />
<Message Text="Cleaned generated files" Importance="high" />
</Target>

<!-- Generate sitemap before build -->
<Target Name="GenerateSitemap" BeforeTargets="Build" Condition="'$(SkipCodeGeneration)' != 'true' AND '$(DesignTimeBuild)' != 'true'">
<Message Text="Generating sitemap..." Importance="high" />
<Exec Command="bash &quot;$(MSBuildProjectDirectory)/generate-sitemap.sh&quot;"
ContinueOnError="false"
WorkingDirectory="$(MSBuildProjectDirectory)"
Condition="Exists('$(MSBuildProjectDirectory)/generate-sitemap.sh')" />
<Message Text="Sitemap generation completed" Importance="high" />
</Target>

<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="8.0.13" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="8.0.13" PrivateAssets="all" />
<PackageReference Include="BlazorWasmPreRendering.Build" Version="6.0.0" />
</ItemGroup>

</Project>
12 changes: 9 additions & 3 deletions Layout/MainLayout.razor
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
@inherits LayoutComponentBase
@using System.Reflection
@using Generated

<div class="page">

Expand Down Expand Up @@ -57,11 +58,16 @@
protected override void OnInitialized()
{
Assembly assembly = typeof(Program).Assembly;
var version = assembly.GetName().Version;
var title = assembly.GetCustomAttribute<AssemblyTitleAttribute>()?.Title ?? "Albatross";
var buildDate = DateTime.Now.ToString("yyyy-MM-dd");

@* BuildInfo = $"{title} v{version}"; *@
// Extract date (YYYYMMDD) from timestamp (YYYYMMDD-HHMM) and use short build ID
var buildDate = BuildConstants.BuildTimestamp.Length >= 8
? BuildConstants.BuildTimestamp.Substring(0, 8)
: BuildConstants.BuildTimestamp;
var shortId = BuildConstants.BuildId.Length >= 8
? BuildConstants.BuildId.Substring(0, 8)
: BuildConstants.BuildId;

BuildInfo = $"{title}";
}
}
21 changes: 12 additions & 9 deletions Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,17 @@
builder.RootComponents.Add<App>("#app");
builder.RootComponents.Add<HeadOutlet>("head::after");

builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
builder.Services.AddScoped<AbuseIPDBService>(sp =>
new AbuseIPDBService(
sp.GetRequiredService<HttpClient>()
)
);

// API access tokens are now hardcoded in the service for security purposes
// This makes them less discoverable than storing them in appsettings.json
ConfigureServices(builder.Services, builder.HostEnvironment.BaseAddress);

await builder.Build().RunAsync();

// Extract service registration to static local function for prerendering support
static void ConfigureServices(IServiceCollection services, string baseAddress)
{
services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(baseAddress) });
services.AddScoped<AbuseIPDBService>(sp =>
new AbuseIPDBService(
sp.GetRequiredService<HttpClient>()
)
);
}
36 changes: 36 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ A modern Blazor WebAssembly application that provides comprehensive IP address a
- **Combined Data Sources**: Integrated AbuseIPDB and Cloudflare Radar API data for comprehensive IP analysis
- **Secure Authentication**: Build-time generated HMAC authentication with timestamp validation for enhanced security
- **CORS Protection**: Cloudflare Worker proxy handles CORS and protects API keys from client exposure
- **SEO-Optimized**: Static HTML prerendering for improved search engine indexing and web crawler accessibility
- **Modern UI**: Clean, responsive Blazor WebAssembly interface with real-time JSON formatting
- **Cross-Platform**: Runs on Windows, macOS, and Linux

Expand Down Expand Up @@ -53,6 +54,41 @@ The project uses MSBuild targets for automated key generation and code injection
- `Generated/build-manifest.json` - Build metadata and timestamps
- `cloudflare-worker.js` - Final worker with injected authentication

## Prerendering

Albatross uses **[BlazorWasmPreRendering.Build](https://github.com/jsakamoto/BlazorWasmPreRendering.Build)** to generate static HTML during the publish process, improving SEO and web crawler accessibility.

### How It Works
- **Build-Time Rendering**: During `dotnet publish`, the app is rendered to static HTML
- **SEO Benefits**: Search engines can index content without executing JavaScript
- **Hidden Content**: Prerendered HTML is hidden (opacity: 0, z-index: -1) to prevent visual flash
- **Blazor Hydration**: The app seamlessly takes over after loading, providing full interactivity
- **Zero Runtime Overhead**: Prerendering happens at build time, not on the server

### Implementation
For prerendering to work, service registration must be extracted into a static local function in `Program.cs`:
```csharp
static void ConfigureServices(IServiceCollection services, string baseAddress)
{
services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(baseAddress) });
services.AddScoped<AbuseIPDBService>(/* ... */);
}
```

See the [BlazorWasmPreRendering.Build documentation](https://github.com/jsakamoto/BlazorWasmPreRendering.Build#services-registration) for more details.

### Verification
To verify prerendering is working after deployment:
```bash
# Check for prerendering markers in the HTML
curl -s https://albatross.devnomadic.com | grep "PRERENDERING-BEGIN"

# Or view page source in browser and search for:
<!-- %%-PRERENDERING-BEGIN-%% -->
```

If prerendering is working, you'll see the full app structure in the HTML source, not just "Loading..."

## Setup and Development

### Prerequisites
Expand Down
52 changes: 0 additions & 52 deletions wwwroot/sitemap.xml
Original file line number Diff line number Diff line change
@@ -1,52 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9
http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd">

<!-- Main Application Page -->
<url>
<loc>https://albatross.devnomadic.com/</loc>
<lastmod>2025-07-12</lastmod>
<changefreq>weekly</changefreq>
<priority>1.0</priority>
</url>

<!-- IP Manifest Files -->
<url>
<loc>https://albatross.devnomadic.com/ip-manifests/AWS.json</loc>
<lastmod>2025-07-12</lastmod>
<changefreq>daily</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>https://albatross.devnomadic.com/ip-manifests/Azure.json</loc>
<lastmod>2025-07-12</lastmod>
<changefreq>daily</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>https://albatross.devnomadic.com/ip-manifests/GCP.json</loc>
<lastmod>2025-07-12</lastmod>
<changefreq>daily</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>https://albatross.devnomadic.com/ip-manifests/Oracle.json</loc>
<lastmod>2025-07-12</lastmod>
<changefreq>daily</changefreq>
<priority>0.8</priority>
</url>

<!-- Robots.txt -->
<url>
<loc>https://albatross.devnomadic.com/robots.txt</loc>
<lastmod>2025-07-12</lastmod>
<changefreq>monthly</changefreq>
<priority>0.3</priority>
</url>

<!-- Note: Search functionality intentionally excluded per robots.txt -->
<!-- AbuseIPDB search endpoints are blocked from crawling -->

</urlset>
Loading