Initial commit
This commit is contained in:
commit
7a9d910d86
187
.gitignore
vendored
Normal file
187
.gitignore
vendored
Normal file
|
@ -0,0 +1,187 @@
|
|||
/packages/
|
||||
/_ReSharper.Caches/
|
||||
|
||||
# Created by https://www.toptal.com/developers/gitignore/api/rider,dotnetcore,jetbrains+all
|
||||
# Edit at https://www.toptal.com/developers/gitignore?templates=rider,dotnetcore,jetbrains+all
|
||||
|
||||
### DotnetCore ###
|
||||
# .NET Core build folders
|
||||
bin/
|
||||
obj/
|
||||
|
||||
# Common node modules locations
|
||||
/node_modules
|
||||
/wwwroot/node_modules
|
||||
|
||||
### JetBrains+all ###
|
||||
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
|
||||
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
||||
|
||||
# User-specific stuff
|
||||
.idea/**/workspace.xml
|
||||
.idea/**/tasks.xml
|
||||
.idea/**/usage.statistics.xml
|
||||
.idea/**/dictionaries
|
||||
.idea/**/shelf
|
||||
|
||||
# Generated files
|
||||
.idea/**/contentModel.xml
|
||||
|
||||
# Sensitive or high-churn files
|
||||
.idea/**/dataSources/
|
||||
.idea/**/dataSources.ids
|
||||
.idea/**/dataSources.local.xml
|
||||
.idea/**/sqlDataSources.xml
|
||||
.idea/**/dynamic.xml
|
||||
.idea/**/uiDesigner.xml
|
||||
.idea/**/dbnavigator.xml
|
||||
|
||||
# Gradle
|
||||
.idea/**/gradle.xml
|
||||
.idea/**/libraries
|
||||
|
||||
# Gradle and Maven with auto-import
|
||||
# When using Gradle or Maven with auto-import, you should exclude module files,
|
||||
# since they will be recreated, and may cause churn. Uncomment if using
|
||||
# auto-import.
|
||||
# .idea/artifacts
|
||||
# .idea/compiler.xml
|
||||
# .idea/jarRepositories.xml
|
||||
# .idea/modules.xml
|
||||
# .idea/*.iml
|
||||
# .idea/modules
|
||||
# *.iml
|
||||
# *.ipr
|
||||
|
||||
# CMake
|
||||
cmake-build-*/
|
||||
|
||||
# Mongo Explorer plugin
|
||||
.idea/**/mongoSettings.xml
|
||||
|
||||
# File-based project format
|
||||
*.iws
|
||||
|
||||
# IntelliJ
|
||||
out/
|
||||
|
||||
# mpeltonen/sbt-idea plugin
|
||||
.idea_modules/
|
||||
|
||||
# JIRA plugin
|
||||
atlassian-ide-plugin.xml
|
||||
|
||||
# Cursive Clojure plugin
|
||||
.idea/replstate.xml
|
||||
|
||||
# Crashlytics plugin (for Android Studio and IntelliJ)
|
||||
com_crashlytics_export_strings.xml
|
||||
crashlytics.properties
|
||||
crashlytics-build.properties
|
||||
fabric.properties
|
||||
|
||||
# Editor-based Rest Client
|
||||
.idea/httpRequests
|
||||
|
||||
# Android studio 3.1+ serialized cache file
|
||||
.idea/caches/build_file_checksums.ser
|
||||
|
||||
### JetBrains+all Patch ###
|
||||
# Ignores the whole .idea folder and all .iml files
|
||||
# See https://github.com/joeblau/gitignore.io/issues/186 and https://github.com/joeblau/gitignore.io/issues/360
|
||||
|
||||
.idea/
|
||||
|
||||
# Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023
|
||||
|
||||
*.iml
|
||||
modules.xml
|
||||
.idea/misc.xml
|
||||
*.ipr
|
||||
|
||||
# Sonarlint plugin
|
||||
.idea/sonarlint
|
||||
|
||||
### Rider ###
|
||||
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
|
||||
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
||||
|
||||
# User-specific stuff
|
||||
|
||||
# Generated files
|
||||
|
||||
# Sensitive or high-churn files
|
||||
|
||||
# Gradle
|
||||
|
||||
# Gradle and Maven with auto-import
|
||||
# When using Gradle or Maven with auto-import, you should exclude module files,
|
||||
# since they will be recreated, and may cause churn. Uncomment if using
|
||||
# auto-import.
|
||||
# .idea/artifacts
|
||||
# .idea/compiler.xml
|
||||
# .idea/jarRepositories.xml
|
||||
# .idea/modules.xml
|
||||
# .idea/*.iml
|
||||
# .idea/modules
|
||||
# *.iml
|
||||
# *.ipr
|
||||
|
||||
# CMake
|
||||
|
||||
# Mongo Explorer plugin
|
||||
|
||||
# File-based project format
|
||||
|
||||
# IntelliJ
|
||||
|
||||
# mpeltonen/sbt-idea plugin
|
||||
|
||||
# JIRA plugin
|
||||
|
||||
# Cursive Clojure plugin
|
||||
|
||||
# Crashlytics plugin (for Android Studio and IntelliJ)
|
||||
|
||||
# Editor-based Rest Client
|
||||
|
||||
# Android studio 3.1+ serialized cache file
|
||||
|
||||
# End of https://www.toptal.com/developers/gitignore/api/rider,dotnetcore,jetbrains+all
|
||||
|
||||
|
||||
# Created by https://www.toptal.com/developers/gitignore/api/macos
|
||||
# Edit at https://www.toptal.com/developers/gitignore?templates=macos
|
||||
|
||||
### macOS ###
|
||||
# General
|
||||
.DS_Store
|
||||
.AppleDouble
|
||||
.LSOverride
|
||||
|
||||
# Icon must end with two \r
|
||||
Icon
|
||||
|
||||
# Thumbnails
|
||||
._*
|
||||
|
||||
# Files that might appear in the root of a volume
|
||||
.DocumentRevisions-V100
|
||||
.fseventsd
|
||||
.Spotlight-V100
|
||||
.TemporaryItems
|
||||
.Trashes
|
||||
.VolumeIcon.icns
|
||||
.com.apple.timemachine.donotpresent
|
||||
|
||||
# Directories potentially created on remote AFP share
|
||||
.AppleDB
|
||||
.AppleDesktop
|
||||
Network Trash Folder
|
||||
Temporary Items
|
||||
.apdisk
|
||||
|
||||
# End of https://www.toptal.com/developers/gitignore/api/macos
|
||||
|
||||
database.db
|
||||
.bearer_token
|
16
zotan.pw-web.sln
Normal file
16
zotan.pw-web.sln
Normal file
|
@ -0,0 +1,16 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "zotan.pw-web", "zotan.pw-web\zotan.pw-web.csproj", "{C13FBB14-C41C-440F-BFB3-61E476E1902D}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{C13FBB14-C41C-440F-BFB3-61E476E1902D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{C13FBB14-C41C-440F-BFB3-61E476E1902D}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{C13FBB14-C41C-440F-BFB3-61E476E1902D}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{C13FBB14-C41C-440F-BFB3-61E476E1902D}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
EndGlobal
|
91
zotan.pw-web/Migrations.cs
Normal file
91
zotan.pw-web/Migrations.cs
Normal file
|
@ -0,0 +1,91 @@
|
|||
using LinqToDB;
|
||||
using LinqToDB.Data;
|
||||
using zotanpw_web.database;
|
||||
using zotanpw_web.database.Tables;
|
||||
|
||||
namespace zotanpw_web;
|
||||
|
||||
public static class Migrations {
|
||||
private const int DbVer = 1;
|
||||
|
||||
private static readonly List<Migration> _migrations = new() {
|
||||
};
|
||||
|
||||
public static void RunMigrations() {
|
||||
using var db = new Database.DbConn();
|
||||
var ccolor = Console.ForegroundColor;
|
||||
|
||||
if (!db.DataProvider.GetSchemaProvider().GetSchema(db).Tables.Any()) {
|
||||
Console.ForegroundColor = ConsoleColor.DarkCyan;
|
||||
Console.Write("Running migration: ");
|
||||
Console.ForegroundColor = ConsoleColor.Yellow;
|
||||
Console.WriteLine("Initialize Database");
|
||||
|
||||
db.CreateTable<DbInfo>();
|
||||
db.CreateTable<TravelynxInfo>();
|
||||
db.CreateTable<AlbumHistoryEntry>();
|
||||
db.CreateTable<PlaylistHistoryEntry>();
|
||||
|
||||
db.InsertWithIdentity(new DbInfo { DbVer = DbVer });
|
||||
}
|
||||
else if (db.DataProvider.GetSchemaProvider().GetSchema(db).Tables.All(t => t.TableName != "DbInfo")) {
|
||||
db.CreateTable<DbInfo>();
|
||||
db.InsertWithIdentity(new DbInfo { DbVer = 0 });
|
||||
}
|
||||
|
||||
Console.ForegroundColor = ConsoleColor.Yellow;
|
||||
Console.WriteLine($"Database version: {db.DbInfo.ToList().First().DbVer}");
|
||||
|
||||
var migrationsToRun = _migrations.FindAll(p => p.IntroducedWithDbVer > db.DbInfo.First().DbVer);
|
||||
if (migrationsToRun.Count == 0) {
|
||||
Console.ForegroundColor = ConsoleColor.Green;
|
||||
Console.WriteLine("No migrations to run.");
|
||||
}
|
||||
else {
|
||||
new Migration(0, "BEGIN TRANSACTION").Run(db);
|
||||
try {
|
||||
migrationsToRun.ForEach(p => p.Run(db));
|
||||
}
|
||||
catch {
|
||||
Console.ForegroundColor = ConsoleColor.DarkRed;
|
||||
Console.WriteLine($"Migrating to database version {DbVer} failed.");
|
||||
new Migration(0, "ROLLBACK TRANSACTION").Run(db);
|
||||
Console.ForegroundColor = ConsoleColor.DarkYellow;
|
||||
Console.WriteLine("Rolled back migrations.");
|
||||
Environment.Exit(1);
|
||||
}
|
||||
|
||||
new Migration(0, "COMMIT TRANSACTION").Run(db);
|
||||
|
||||
var newdb = new Database.DbConn();
|
||||
var dbinfo = newdb.DbInfo.First();
|
||||
dbinfo.DbVer = DbVer;
|
||||
newdb.Update(dbinfo);
|
||||
|
||||
Console.ForegroundColor = ConsoleColor.DarkGreen;
|
||||
Console.WriteLine($"Database version is now: {DbVer}");
|
||||
Console.ForegroundColor = ConsoleColor.Green;
|
||||
Console.WriteLine("Finished running migrations.");
|
||||
}
|
||||
|
||||
Console.ForegroundColor = ccolor;
|
||||
}
|
||||
|
||||
private class Migration {
|
||||
private readonly string _sql;
|
||||
public readonly int IntroducedWithDbVer;
|
||||
|
||||
public Migration(int introducedWithDbVer, string sql) {
|
||||
IntroducedWithDbVer = introducedWithDbVer;
|
||||
_sql = sql;
|
||||
}
|
||||
|
||||
public void Run(DataConnection db) {
|
||||
Console.ForegroundColor = ConsoleColor.DarkCyan;
|
||||
Console.Write("Running migration: ");
|
||||
Console.ForegroundColor = ConsoleColor.Yellow;
|
||||
Console.WriteLine(_sql);
|
||||
db.Execute(_sql);
|
||||
}
|
||||
}
|
||||
}
|
25
zotan.pw-web/Pages/Error.cshtml
Normal file
25
zotan.pw-web/Pages/Error.cshtml
Normal file
|
@ -0,0 +1,25 @@
|
|||
@page
|
||||
@model ErrorModel
|
||||
@{
|
||||
ViewData["Title"] = "error";
|
||||
}
|
||||
|
||||
<h1 class="text-danger">Error.</h1>
|
||||
<h2 class="text-danger">An error occurred while processing your request.</h2>
|
||||
|
||||
@if (Model.ShowRequestId) {
|
||||
<p>
|
||||
<strong>Request ID:</strong> <code>@Model.RequestId</code>
|
||||
</p>
|
||||
}
|
||||
|
||||
<h3>Development Mode</h3>
|
||||
<p>
|
||||
Swapping to the <strong>Development</strong> environment displays detailed information about the error that occurred.
|
||||
</p>
|
||||
<p>
|
||||
<strong>The Development environment shouldn't be enabled for deployed applications.</strong>
|
||||
It can result in displaying sensitive information from exceptions to end users.
|
||||
For local debugging, enable the <strong>Development</strong> environment by setting the <strong>ASPNETCORE_ENVIRONMENT</strong> environment variable to <strong>Development</strong>
|
||||
and restarting the app.
|
||||
</p>
|
23
zotan.pw-web/Pages/Error.cshtml.cs
Normal file
23
zotan.pw-web/Pages/Error.cshtml.cs
Normal file
|
@ -0,0 +1,23 @@
|
|||
using System.Diagnostics;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
|
||||
namespace zotanpw_web.Pages;
|
||||
|
||||
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
|
||||
[IgnoreAntiforgeryToken]
|
||||
public class ErrorModel : PageModel {
|
||||
public string? RequestId { get; set; }
|
||||
|
||||
public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
|
||||
|
||||
private readonly ILogger<ErrorModel> _logger;
|
||||
|
||||
public ErrorModel(ILogger<ErrorModel> logger) {
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public void OnGet() {
|
||||
RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier;
|
||||
}
|
||||
}
|
162
zotan.pw-web/Pages/Index.cshtml
Normal file
162
zotan.pw-web/Pages/Index.cshtml
Normal file
|
@ -0,0 +1,162 @@
|
|||
@page
|
||||
@using zotanpw_web.database
|
||||
@model IndexModel
|
||||
@{
|
||||
ViewData["title"] = "home";
|
||||
var travelynx = new Database.DbConn().TravelynxInfo.ToList();
|
||||
}
|
||||
|
||||
@section head
|
||||
{
|
||||
<h2 style="margin-bottom: 0;">Welcome internet user.</h2>
|
||||
<small style="color:#aaa">An experimental http3 + v6o version of this page is available <a href="https://h3.zotan.pw">here</a>.</small>
|
||||
}
|
||||
|
||||
<h1 id="about">About me</h1>
|
||||
<p>Hey there, I'm <span style="color: #D291BC;">Laura</span> (~<span style="color: #957DAD">zotan</span>, she/they), and I'm a queer, anarchist & antifascist network engineer, system administrator and software developer.</p>
|
||||
<p>
|
||||
You can find me in hackerspaces across Germany, primarily in Berlin, Munich and Karlsruhe.
|
||||
@if (travelynx.Any()) {
|
||||
var status = travelynx.First();
|
||||
if (status.CheckedIn) {
|
||||
if (string.IsNullOrWhiteSpace(status.Train)) {
|
||||
<span>Most recently, I was seen on <span style="color: #D291BC;">an unknon train somewhere in Germany</span>.</span>
|
||||
}
|
||||
else if (string.IsNullOrWhiteSpace(status.Destination)) {
|
||||
<span>Most recently, I was seen on <span style="color: #D291BC;">@status.Train</span>.</span>
|
||||
}
|
||||
else {
|
||||
<span>Most recently, I was seen on <span style="color: #D291BC;">@status.Train</span> on the way to <span style="color: #b295cf">@status.Destination</span>.</span>
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (string.IsNullOrWhiteSpace(status.Destination)) {
|
||||
<span>Most recently, I was seen at <span style="color: #b295cf;">an unknon train station in Germany</span>.</span>
|
||||
}
|
||||
else {
|
||||
<span>Most recently, I was seen at <span style="color: #b295cf;">@status.Destination</span>.</span>
|
||||
}
|
||||
}
|
||||
}
|
||||
</p>
|
||||
<p>Here you can find my contact info, crypto keys, projects, and a few links.</p>
|
||||
|
||||
@if (Request.Headers["X-Forwarded-For"] == "::1") {
|
||||
<p style="background-color:#333; padding:5px 7px;">Many of my services are IPv6-only, and more are soon to follow. Your browser preferred IPv4 while connecting to this website, so please <a href="https://ip6.biz" target="_blank">check your connection</a> for IPv6 support before contacting me if your browser displays a network error.</p>
|
||||
}
|
||||
|
||||
<h2 id="contact">Contact</h2>
|
||||
<p>You can contact me via the following services:</p>
|
||||
<ul>
|
||||
<li>
|
||||
<strong>Telegram:</strong> <a href="https://t.me/zotan" target="_blank">@@zotan</a>
|
||||
</li>
|
||||
<li>
|
||||
<strong>Threema: </strong> <a href="https://threema.id/S59S9U8J" target="_blank">S59S9U8J</a>
|
||||
</li>
|
||||
<li>
|
||||
<strong>Matrix: </strong> <a href="https://matrix.to/#/@@zotan:161.rocks" target="_blank">@@zotan:161.rocks</a>
|
||||
</li>
|
||||
<li>
|
||||
<strong>Email: </strong> <a href="mailto:zotan@zotan.pw" target="_blank">zotan@zotan.pw</a> (for GPG see below)<br/>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h2 id="social">Profiles</h2>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="/blog">Blog</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/np">Now playing</a>
|
||||
</li>
|
||||
</ul>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="https://estrogen.network/@@zotan" target="_blank">Fediverse</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://chaos.stream/profile/zotan" target="_blank">Live streaming</a> (occasional gaming and photo editing streams)
|
||||
</li>
|
||||
</ul>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="https://git.ztn.sh/zotan" target="_blank">Gitea</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://github.com/zotanmew" target="_blank">GitHub</a>
|
||||
</li>
|
||||
</ul>
|
||||
<h2 id="links">Network</h2>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="https://status.zotan.network" target="_blank">Network status</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://status.zotan.services" target="_blank">Service status</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://vnstat.zotan.services" target="_blank">Network load</a>
|
||||
</li>
|
||||
</ul>
|
||||
<h2 id="photo">Photography</h2>
|
||||
<ul>
|
||||
<li><a href="https://zotan.photos" target="_blank">Portfolio</a> (my favourite shots)</li>
|
||||
<li><a href="https://t.me/photolaura" target="_blank">Main photo channel</a> (all of my photos)</li>
|
||||
<li><a href="https://t.me/photolaura_nofood" target="_blank">Secondary photo channel</a> (all of my photos that don't contain food)</li>
|
||||
</ul>
|
||||
<h2 id="projects-net">Projects - Networking</h2>
|
||||
<ul>
|
||||
<li><a href="https://zotan.network" target="_blank">AS211579</a> ~ zotan experimental networks</li>
|
||||
</ul>
|
||||
<h2 id="projects-web">Projects - Webservices</h2>
|
||||
<ul>
|
||||
<li><a href="https://chaos.stream" target="_blank">chaos.stream</a> - a chaos-community centric live streaming platform</li>
|
||||
<li><a href="https://c3stream.de" target="_blank">c3stream.de</a> - a media.ccc.de mirror with added functionality</li>
|
||||
<li><a href="https://ip6.biz" target="_blank">ip6.biz</a> - IPv6 (and IPv4) address info and connection test</li>
|
||||
</ul>
|
||||
<ul>
|
||||
<li><a href="https://hc.ztn.sh" target="_blank">Healthchecks.io instance</a> (free, open signups)</li>
|
||||
<li>
|
||||
<a href="https://transit.ztn.sh" target="_blank">Öffisearch instance</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://paste.ztn.sh" target="_blank">PrivateBin instance</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://t.ztn.sh" target="_blank">Nitter instance</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://meet.ztn.sh" target="_blank">Jitsi instance</a>
|
||||
</li>
|
||||
</ul>
|
||||
<h2 id="projects-soft">Projects - Software</h2>
|
||||
<ul>
|
||||
<li><a href="https://git.ztn.sh/zotan/nginx-mod-rtmp" target="_blank">nginx-mod-rtmp</a> - fork of the original with fixed bugs and added functionality</li>
|
||||
<li><a href="https://git.ztn.sh/zotan/rtmpdash" target="_blank">RTMPdash</a> - the software powering <a href="https://chaos.stream">chaos.stream</a></li>
|
||||
<li><a href="https://git.ztn.sh/zotan/ip6.biz" target="_blank">ip6.biz</a> - the source code for the IPv6 toolbox mentioned above</li>
|
||||
<li><a href="https://git.ztn.sh/zotan/monithor" target="_blank">monithor</a> - alerting and status page generator for InfluxDB</li>
|
||||
<li><a href="https://git.ztn.sh/zotan/webmusic" target="_blank">webmusic</a> - a simple web player for your music</li>
|
||||
<li><a href="https://git.ztn.sh/zotan/trainav" target="_blank">trainav</a> - experimental train journey planner</li>
|
||||
<li><a href="https://git.ztn.sh/zotan/repomgr" target="_blank">repomgr</a> - experimental AUR buildserver and repo manager</li>
|
||||
<li><a href="https://git.ztn.sh/zotan/autotag" target="_blank">autotag</a> - experimental music tag normalizer</li>
|
||||
<li><a href="https://git.ztn.sh/zotan/mediamanager" target="_blank">mediamanager</a> - experimental media tracker</li>
|
||||
</ul>
|
||||
<h2 id="projects-hard">Projects - Hardware</h2>
|
||||
<ul>
|
||||
<li><a href="https://git.ztn.sh/zotan/esp32-co2-mhz19b" target="_blank">esp32-co2-mhz19b</a> - ESP32-based CO2 monitoring setup</li>
|
||||
</ul>
|
||||
<h2 id="papers">Papers</h2>
|
||||
<ul>
|
||||
<li>2019 - Comparison of music streaming services’ encryption concepts <a href="/files/StreamingCrypto.pdf" target="_blank">Download</a></li>
|
||||
<li>2020 - Programming a modular Smart-Home-System (prescientific paper) <a href="/files/SmartHome.pdf" target="_blank">Download</a></li>
|
||||
</ul>
|
||||
<h2 id="crypto">Crypto</h2>
|
||||
<p>I use the following GPG keys:</p>
|
||||
<ul>
|
||||
<li><strong>Primary (ECC):</strong> F8A5 DAC0 0E43 5119 2089 42F9 D044 E84C 5BE0 1605 <a href="/files/primary.gpg">Pubkey</a></li>
|
||||
</ul>
|
||||
|
||||
@section postfooter {
|
||||
<img src="/files/help.svg" height=450rem title="Designed by someone I love, ~fr2">
|
||||
}
|
14
zotan.pw-web/Pages/Index.cshtml.cs
Normal file
14
zotan.pw-web/Pages/Index.cshtml.cs
Normal file
|
@ -0,0 +1,14 @@
|
|||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
|
||||
namespace zotanpw_web.Pages;
|
||||
|
||||
public class IndexModel : PageModel {
|
||||
private readonly ILogger<IndexModel> _logger;
|
||||
|
||||
public IndexModel(ILogger<IndexModel> logger) {
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public void OnGet() { }
|
||||
}
|
33
zotan.pw-web/Pages/NowPlaying.cshtml
Normal file
33
zotan.pw-web/Pages/NowPlaying.cshtml
Normal file
|
@ -0,0 +1,33 @@
|
|||
@page "/np"
|
||||
@model NowPlayingModel
|
||||
|
||||
@{
|
||||
ViewData["title"] = "now playing";
|
||||
}
|
||||
|
||||
<p>Here you can see what kind of music I've been listening to lately. This table is updated every couple minutes from multiple data sources.</p>
|
||||
<p style="background-color:#333; padding:5px 7px;">Links to my music library / webplayer are accessible to authorized users only. If you think this includes you, but something isn't working, <a href="/#contact">DM me</a>.</p>
|
||||
<ul>
|
||||
<li>Albums</li>
|
||||
</ul>
|
||||
<h3>>> Albums</h3>
|
||||
<table>
|
||||
<th>Artist</th>
|
||||
<th>Album</th>
|
||||
<th>Link</th>
|
||||
<tr>
|
||||
<td>Röyksopp</td>
|
||||
<td>Profound Mysteries</td>
|
||||
<td><a href="https://music.zotan.services/?/R%C3%B6yksopp/Profound%20Mysteries">music.zotan.services</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>t+pazolite</td>
|
||||
<td>Refactoring Travel</td>
|
||||
<td><a href="https://music.apple.com/gb/album/%E3%83%AA%E3%83%95%E3%82%A1%E3%82%AF%E3%82%BF%E3%83%AA%E3%83%B3%E3%82%B0-%E3%83%88%E3%83%A9%E3%83%99%E3%83%AB/1494424249">Apple Music</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>test</td>
|
||||
<td>test</td>
|
||||
<td>test</td>
|
||||
</tr>
|
||||
</table>
|
9
zotan.pw-web/Pages/NowPlaying.cshtml.cs
Normal file
9
zotan.pw-web/Pages/NowPlaying.cshtml.cs
Normal file
|
@ -0,0 +1,9 @@
|
|||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
|
||||
namespace zotanpw_web.Pages;
|
||||
|
||||
public class NowPlayingModel : PageModel {
|
||||
public void OnGet() {
|
||||
|
||||
}
|
||||
}
|
36
zotan.pw-web/Pages/Shared/_Layout.cshtml
Normal file
36
zotan.pw-web/Pages/Shared/_Layout.cshtml
Normal file
|
@ -0,0 +1,36 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<meta http-equiv="X-UA-Compatible" content="chrome=1">
|
||||
<meta name="twitter:card" content="summary"/>
|
||||
<meta name="twitter:title" content="@ViewData["card_title"]"/>
|
||||
<meta name="twitter:description" content="@ViewData["card_desc"]"/>
|
||||
<title>zotan.pw >> @ViewData["title"]</title>
|
||||
<link rel="stylesheet" href="~/css/site.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3">
|
||||
<div class="container">
|
||||
<h1>
|
||||
<a href="/">zotan.pw >> @ViewData["Title"]</a>
|
||||
</h1>
|
||||
@await RenderSectionAsync("head", required: false)
|
||||
</div>
|
||||
</nav>
|
||||
</header>
|
||||
<div class="container">
|
||||
<section id="main_content">
|
||||
@RenderBody()
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<div style="text-align: center;" >
|
||||
@await RenderSectionAsync("prefooter", required: false)
|
||||
<p style="color: #666666">--- Served by @Environment.MachineName running <a class="footerlink" href="https://git.ztn.sh/zotan/zotan.pw-web/commit/@Utils.LinkVersion" target="_blank">zotan.pw-web @Utils.Version</a> on .NET @Environment.Version ---</p>
|
||||
@await RenderSectionAsync("postfooter", required: false)
|
||||
</div>
|
||||
<script src="~/js/site.js"></script>
|
||||
</body>
|
||||
</html>
|
3
zotan.pw-web/Pages/_ViewImports.cshtml
Normal file
3
zotan.pw-web/Pages/_ViewImports.cshtml
Normal file
|
@ -0,0 +1,3 @@
|
|||
@using zotanpw_web
|
||||
@namespace zotanpw_web.Pages
|
||||
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
|
3
zotan.pw-web/Pages/_ViewStart.cshtml
Normal file
3
zotan.pw-web/Pages/_ViewStart.cshtml
Normal file
|
@ -0,0 +1,3 @@
|
|||
@{
|
||||
Layout = "_Layout";
|
||||
}
|
14
zotan.pw-web/Pages/blog/Blog.cshtml
Normal file
14
zotan.pw-web/Pages/blog/Blog.cshtml
Normal file
|
@ -0,0 +1,14 @@
|
|||
@page "/blog"
|
||||
@model BlogModel
|
||||
@{
|
||||
ViewData["title"] = "blog";
|
||||
}
|
||||
|
||||
<p>Hey there, welcome to the blog of a disabled neurodivergent queer person unhappy with the state of the world.</p>
|
||||
<p>This is where I post about things that make it somewhat fun, things that help me with life in general or just things I felt like sharing.</p>
|
||||
<h1 id="posts">Posts</h1>
|
||||
<ul>
|
||||
@foreach (var post in BlogModel.Posts) {
|
||||
<li><strong>@post.PublishedOn.ToString("yyyy-MM-dd")</strong> <a href="/blog/@post.Shorthand">@post.Title</a></li>
|
||||
}
|
||||
</ul>
|
40
zotan.pw-web/Pages/blog/Blog.cshtml.cs
Normal file
40
zotan.pw-web/Pages/blog/Blog.cshtml.cs
Normal file
|
@ -0,0 +1,40 @@
|
|||
using System.Text.RegularExpressions;
|
||||
using Markdig;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
|
||||
namespace zotanpw_web.Pages.blog;
|
||||
|
||||
public class BlogModel : PageModel {
|
||||
public static readonly List<BlogPost> Posts = new() {
|
||||
new BlogPost("adhd-and-notes", "ADHD & Notetaking: an autistic perspective", DateOnly.Parse("2021-08-07")),
|
||||
new BlogPost("ipv6-networking", "IPv6-native networking: a project report", DateOnly.Parse("2021-08-23")),
|
||||
};
|
||||
|
||||
static BlogModel() {
|
||||
Posts = Posts.OrderByDescending(p => p.PublishedOn).ToList();
|
||||
}
|
||||
|
||||
public void OnGet() { }
|
||||
|
||||
public class BlogPost {
|
||||
public readonly string Title;
|
||||
public readonly string Shorthand;
|
||||
public readonly DateOnly PublishedOn;
|
||||
public int ReadTimeMinutes;
|
||||
public string Content = "";
|
||||
|
||||
public BlogPost(string shorthand, string title, DateOnly publishedOn) {
|
||||
Title = title;
|
||||
PublishedOn = publishedOn;
|
||||
Shorthand = shorthand;
|
||||
UpdateContent();
|
||||
}
|
||||
|
||||
public void UpdateContent() {
|
||||
var markdownText = System.IO.File.ReadAllText($"Pages/blog/posts/{Shorthand}.md");
|
||||
|
||||
Content = Markdown.ToHtml(markdownText, new MarkdownPipelineBuilder().UseGenericAttributes().Build());
|
||||
ReadTimeMinutes = Regex.Matches(markdownText, @"\b\w+\b").Count / 150;
|
||||
}
|
||||
}
|
||||
}
|
22
zotan.pw-web/Pages/blog/BlogPost.cshtml
Normal file
22
zotan.pw-web/Pages/blog/BlogPost.cshtml
Normal file
|
@ -0,0 +1,22 @@
|
|||
@page "/blog/{post}"
|
||||
|
||||
@{
|
||||
if (string.IsNullOrWhiteSpace((string)RouteData.Values["post"]!)) {
|
||||
return;
|
||||
}
|
||||
var post = BlogModel.Posts.FirstOrDefault(p => p.Shorthand == (string)RouteData.Values["post"]!);
|
||||
if (post == null) {
|
||||
Response.Redirect("/Error");
|
||||
return;
|
||||
}
|
||||
ViewData["title"] = $"blog >> {post.Shorthand}";
|
||||
#if (DEBUG)
|
||||
post.UpdateContent();
|
||||
#endif
|
||||
}
|
||||
|
||||
<b>@post.PublishedOn.ToString("yyyy-MM-dd")</b> - @Utils.a_an(post.ReadTimeMinutes) @post.ReadTimeMinutes minute read (150 wpm)
|
||||
<h1 id="post">IPv6-native networking: a project report</h1>
|
||||
<div align="justify">
|
||||
@Html.Raw(post.Content)
|
||||
</div>
|
133
zotan.pw-web/Pages/blog/posts/adhd-and-notes.md
Normal file
133
zotan.pw-web/Pages/blog/posts/adhd-and-notes.md
Normal file
|
@ -0,0 +1,133 @@
|
|||
If you are living with ADHD, diagnosed or not, the following things
|
||||
might sound familiar: *"I forgot to write that down"*, *"I forgot to do
|
||||
that"*, *"I don't remember that"*.
|
||||
|
||||
If you ask neurotypical people what they do to resolve that, they will
|
||||
probably give you answers ranging from "Oh I just have it all in my
|
||||
head" to "Just use a todo list / GTD system / bullet journal", both
|
||||
equally unhelpful to most neurodiverse folks I know.
|
||||
|
||||
Reading [this article](https://xeiaso.net/blog/gtd-on-paper-2021-06-13){target="_blank"} by Xe inspired me to tackle this
|
||||
problem for myself. (I highly encourage you to read the linked post
|
||||
along with the rest of their blog)
|
||||
|
||||
Now, back to the topic at hand. As mentioned, there are many common
|
||||
strategies for managing tasks and notes, many of which simply do not
|
||||
work for me, but let's go through the why and try to find something that
|
||||
works from there.
|
||||
|
||||
### Journals, paper and other physical ways of notetaking
|
||||
|
||||
The most immediate problem with this one is something many ADHD folks
|
||||
will know very well - keeping track of the physical thing. Many times
|
||||
have I lost track of notebooks, journals, diaries or anything related,
|
||||
often times not finding them again to this day. For something I have to
|
||||
rely on (physical extension of my brain, my memories, my thoughts),
|
||||
that's bad. It's hard to forget your head, after all - even though a
|
||||
certain figure of speech might suggest otherwise.
|
||||
|
||||
Another issue, that links more into the *autistic perspective* part of
|
||||
the title, is the thing that many people like about paper - its
|
||||
append-only nature. I have very specific ideas about how I want things
|
||||
structured - and those ideas and needs vary with time and with the
|
||||
contents of the page. You just can't (easily and realistically) re-write
|
||||
the entire page every time those change, which makes paper inconvenient
|
||||
at best and irritating at worst.
|
||||
|
||||
But there are also some wonderful things about paper, some even come as
|
||||
a direct consequence of the problems I just described. You can just
|
||||
start writing, there are no creative restrictions on what you can and
|
||||
can't write, draw or otherwise do with the page, there is no fixed set
|
||||
of design choices, style guidelines and whatnot. The append-only nature
|
||||
also forces you to stop worrying about mistakes, and ideally should let
|
||||
you be in full control of writing out thoughts.
|
||||
|
||||
To summarize: Paper is problematic because of the physical and
|
||||
append-only nature, but can also great because of the freedom and
|
||||
implicit restrictions it brings onto the table. What I then set out to
|
||||
do is translating those concepts into the digital world, as closely
|
||||
adhering to those concepts (and other things my brain likes that I
|
||||
didn't cover in this post) as possible.
|
||||
|
||||
### Markdown and the digital world
|
||||
|
||||
Once you dabble with digital notetaking for more than a few minutes,
|
||||
there's no way not to stumble upon Markdown. And there's good reason for
|
||||
that, being an easy to understand, simple and human-readable (in
|
||||
contrast to programmer-readable) markup language.
|
||||
|
||||
Those things also bring caveats with them, however. Simplicity
|
||||
inherently means limitation, and that's also true here. There isn't that
|
||||
much most Markdown renderers can do. Even worse, there is fragmentation
|
||||
in the Markdown space, with plain Markdown, GitHub-flavored Markdown
|
||||
(GFM) and MultiMarkdown as examples, not to speak of the variety of ways
|
||||
different renderers for the same specification actually interpret
|
||||
things.
|
||||
|
||||
Where does that leave us, then? I think Markdown is great, just not the
|
||||
full story we need here. It's a great starting point, and that's why my
|
||||
personal solution builds upon Markdown. So what is it that I currently
|
||||
(and thus far successfully, i.e. there when I need it, how I need it, as
|
||||
I want it) use?
|
||||
|
||||
It's a combination of [Obsidian](https://obsidian.md){target="_blank"} (a fancy
|
||||
Markdown editor, self-proclaimed "second brain"), some plugins, a custom
|
||||
theme, and most importantly, *not using the Markdown __renderer__*. You
|
||||
might wonder how that works, isn't markdown supposed to be rendered? To
|
||||
which I say - yes, but we can do better. The one thing you are losing
|
||||
with that click of a button is control. Suddenly you have the version of
|
||||
what you wrote in front of you that the renderer decided on, not how you
|
||||
wanted it to look and feel. Which defeats the entire purpose of this
|
||||
project, to get something that offers creative freedom close to physical
|
||||
paper, without being convoluted to use.
|
||||
|
||||
### Putting it all together
|
||||
So how does my setup look like exactly? Like
|
||||
[this](/files/blog/adhd-and-notes.png){target="_blank"}. Let me
|
||||
explain what you are looking at here. On the left there is a tree view
|
||||
of the directory structure that is currently open in Obsidian.
|
||||
|
||||
From top to bottom: *Events* are things like conferences and similar,
|
||||
*Journal* is where the daily notes go, *Knowledge* is a categorized map
|
||||
of information and trivia that might be useful again in the future,
|
||||
*Meta* is stuff relevant for debugging Obsidian itself, *Notes* are
|
||||
uncategorized but titled notes, *People* is for keeping track of people
|
||||
I know (for the non-ADHD people reading this, yes, this is necessary, I
|
||||
regularly forget basic things about people very close to me), *Places*
|
||||
is the same thing but for Places like restaurants and stuff (important
|
||||
to keep track of what I eat and where to get it and stuff), *Projects*
|
||||
is pretty self-explanatory, *Vault* is the "system folder" where all the
|
||||
templates and attachments go, and *Zettelkasten* is for untitled notes.
|
||||
I have a shortcut configured that will create one of those untitled
|
||||
notes so I can just type out a thought and figure out what it's about
|
||||
later.
|
||||
|
||||
The file that's open is the daily journal template. This is used to
|
||||
automatically generate the daily journal entry when I click on a date in
|
||||
the calendar applet you can see on the top right. I then type out basic
|
||||
info about the day (where I was when I woke up, when I woke up) and move
|
||||
over incomplete TODOs from yesterday's daily note. You will also notice
|
||||
the text editor is, well, in edit mode, with a nice monospace font. This
|
||||
allows me to customize the spacing of individual elements in the
|
||||
documents however I want (just like paper), which would all get lost
|
||||
when rendering to HTML.
|
||||
|
||||
### Summary and conclusion
|
||||
|
||||
This setup allows me to write freely, structure everything the same way
|
||||
my brain is structured, keep track of what I've been doing, keep track
|
||||
of things that still need to be done, and much more. Have I forgotten
|
||||
about it? Yes - two times over the past month. In comparison to previous
|
||||
methods, this is great! It's also fairly easy to reconstruct the past
|
||||
day or two, so I think I'm doing okay.
|
||||
|
||||
I won't pretend that this system will work for everyone, but I do hope
|
||||
that you will find some useful information in this writeup. If you have
|
||||
any questions (or want me to help you with Markdown, Obsidian or any
|
||||
other part of this setup), feel free to contact me. (Links for that are
|
||||
on the [main site](/#contact))
|
||||
|
||||
I hope this post was interesting for you, being the first time I've ever
|
||||
written one like it. If you have any comments on the blog or my writing
|
||||
style or just this post in general, please contact me as well. In any
|
||||
case, thanks for reading and have a wonderful day!
|
170
zotan.pw-web/Pages/blog/posts/ipv6-networking.md
Normal file
170
zotan.pw-web/Pages/blog/posts/ipv6-networking.md
Normal file
|
@ -0,0 +1,170 @@
|
|||
If you have reached this post, chances are you already know my [AS211579](https://zotan.network){target="_blank"} project.
|
||||
This post serves as a summary of the things I learnt and the roadblocks
|
||||
I had to overcome on the way to get the network to its current state.
|
||||
|
||||
### Goals and setup
|
||||
|
||||
For those who don't know the project, here's a quick recap. After having
|
||||
dabbled a bit with [DN42](https://dn42.eu){target="_blank"} last year during the first lockdown, I wanted to do the real thing, in
|
||||
the same global routing system your ISP is using to reach this very web
|
||||
server. While this sounds unnecessarily convoluted and complicated to
|
||||
accomplish, it was actually pretty easy and not that expensive. The easy
|
||||
part is almost completely due to my knowledge gained by interacting with
|
||||
DN42 and the awesome people in #dn42 on the [hackint](https://hackint.org){target="_blank"} IRC, who have
|
||||
helped me with debugging the stupidest of mistakes. If you're interested
|
||||
in any of the things I'm about to talk about, be sure to check out the
|
||||
community behind it, it's seriously amazing.
|
||||
|
||||
Alright, we've established the goals, where do we go from here? I
|
||||
quickly found a sponsoring AS (something you need for the *cheap* part)
|
||||
and had all the documents on the way to RIPE (the regional internet
|
||||
registry responsible for, among others, Europe and Asia). Once all that
|
||||
was processed, a nice person from one of the many network group chats
|
||||
I'm in (Wim, if you are reading this, thank you so much), hooked me up
|
||||
with a free /40 IPv6 subnet for all my routing needs. Next, I got BGP
|
||||
VMs. Good places to get them are either
|
||||
[Vultr](https://vultr.com){target="_blank"} (cloud provider)
|
||||
or various smaller providers like
|
||||
[iFog](https://ifog.ch){target="_blank"}. Peering mostly
|
||||
happened on [LocIX](https://locix.online){target="_blank"}.
|
||||
From there, I provided connectivity to home routers, laptops and other
|
||||
computer-y devices via WireGuard.
|
||||
|
||||
### Going a step further
|
||||
|
||||
Once my projects (mostly RPKI) eclipsed the performance level provided
|
||||
by the VMs, I contacted the nice people at
|
||||
[Meerfarbig](https://meerfarbig.net){target="_blank"} for a
|
||||
dedicated machine. That one is the primary server running the network to
|
||||
this day. If you are trying to set up a similar thing and are looking
|
||||
for specifics, feel free to [contact me](/#contact) and I will give you
|
||||
appropriate resources.
|
||||
|
||||
### The trials and tribulations of networking without IPv4
|
||||
|
||||
Now for the fun part, and the likely reason you are here in the first
|
||||
place: all the things that broke along the way. You see, as you might be
|
||||
able to tell from the title, my project goal was to set up an
|
||||
IPv6-native network. That implies NAT64, DNS64, 464XLAT and a whole bag
|
||||
of other fun things. But let's start at the beginning.
|
||||
|
||||
When you have IPv6-only networks, especially when talking about eyeball
|
||||
networks (the kind mostly used for content consumption, e.g. viewing
|
||||
webpages and their content), you will want a way to reach IPv4-only
|
||||
servers. Many popular websites still presently don't support IPv6. At
|
||||
time of writing, this includes GitHub, which is fairly important for
|
||||
developing things. A less important (but still relevant) example is
|
||||
Reddit. For now, we can't reach those websites. What do we do from here?
|
||||
|
||||
Our (first) solution is called NAT64, which translates packets between
|
||||
IPv6 and IPv4, hence the name. The software I chose for this task is
|
||||
[Jool](https://jool.mx){target="_blank"}. Setting it up was
|
||||
fairly trivial, and I quickly set up three redundant NAT64-gateways that
|
||||
announce the NAT64-WKP (well-known prefix, <span
|
||||
class="highlight-bg">64:ff9b::/96</span>). So far so good, but how do we
|
||||
get our systems to actually *use* those gateways?
|
||||
|
||||
For that, we need to look at DNS, which is responsible for translating
|
||||
our domain names (e.g. <span class="highlight-bg">github.com</span>) to
|
||||
an address we can connect to (e.g. <span
|
||||
class="highlight-bg">140.82.121.3</span>). If we configure the resolver
|
||||
that does that lookup to synthesize an AAAA record for our IPv4-only
|
||||
domain, we can connect to it! And that's what I'm doing. Using
|
||||
<a href="https://www.nlnetlabs.nl/projects/unbound/about/"
|
||||
target="_blank">unbound</a>, the address <span
|
||||
class="highlight-bg">140.82.121.3</span> (from the real A record) is
|
||||
translated to <span class="highlight-bg">64:ff9b::140.82.121.3</span>,
|
||||
or <span class="highlight-bg">64:ff9b::8c52:7903</span> in encoded form,
|
||||
and returned as a synthesized AAAA record. Once we configure our system
|
||||
to use this resolver, *most* IPv4-only sites work perfectly! If you are
|
||||
asking why I said most there, I hope you are in for a ride.
|
||||
|
||||
### 464XLAT and questioning whether connecting computers together was a good idea
|
||||
|
||||
The answer to that question is no, obviously. And it keeps chipping away
|
||||
at [my sanity](https://xkcd.com/2259/){target="_blank"}. But
|
||||
since we're here, I might as well roll with it. So, what does 464XLAT
|
||||
even mean? It's a specific combination of systems in place to ensure
|
||||
connectivity to IPv4 hosts from IPv6-only networks. Namely, in addition
|
||||
to DNS64 and the NAT64 gateway (called PLAT in this setup, provider-side
|
||||
address translator), we need a second piece of software, the CLAT
|
||||
(client/customer-side address translator).
|
||||
|
||||
Why, you ask? Because lots of rather popular software (for example,
|
||||
Skype and Spotify) not only use IPv4, but hardcoded IPv4 literals. That
|
||||
means that instead of connecting to <span
|
||||
class="highlight-bg">somedomain.tld</span>, the software tries to
|
||||
connect to <span class="highlight-bg">192.0.2.255</span>, which will
|
||||
fail without a CLAT, since we are only capturing (and synthesizing AAAA
|
||||
records for) DNS queries. A CLAT will take that packet and translate it
|
||||
to an IPv6 packet destined for the DNS64-synthesized address of the
|
||||
target host, the same one the resolver would have synthesized, had we
|
||||
not used literals.
|
||||
|
||||
Okay, sounds simple enough, how do we do this? Most mobile operating
|
||||
systems (Android and iOS) and some desktop operating systems (Windows)
|
||||
support this natively, though support outside of cellular connections is
|
||||
limited to non-existent. Since we are working with WireGuard here, this
|
||||
won't help us. The solution I used here is giving all clients a private
|
||||
IPv4 address, and instead running the CLAT on the router the tunnel
|
||||
terminates on. For compatibility reasons I use addresses from the prefix
|
||||
<span class="highlight-bg">100.64.0.0/10</span> meant for CGNAT (which
|
||||
is almost what we are doing here) for this purpose.
|
||||
|
||||
### Trying to get it all to work
|
||||
|
||||
After the configuration part, getting NAT64 to run was fairly easy,
|
||||
despite some initial issues with routing the NAT64-WKP. Once I turned on
|
||||
464XLAT however, everything broke. <span class="highlight-bg">::1</span>
|
||||
(the IPv6 loopback address) was unreachable. Traceroutes that shouldn't
|
||||
even have gone through the 464XLAT stopped working. I ran into bugs in
|
||||
Jool. Two fairly major ones, to be exact. However, the developers were
|
||||
very helpful in debugging the problems, and got both of them resolved
|
||||
within about two months (shoutouts to ydahhrk!). If you intend on
|
||||
deploying a similar setup, I recommend going for the -git version, since
|
||||
those bugs are fixed there already.
|
||||
|
||||
### One more thing
|
||||
|
||||
One last roadblock was wireguard-quick for macOS. For those unfamiliar
|
||||
with these issues, I'm glad you didn't go through that debugging rabbit
|
||||
hole. To start with, if you are tunneling all of your traffic and your
|
||||
WiFi or Ethernet connection doesn't support IPv6 while your tunnel does,
|
||||
macOS will sometimes decide to be smart and not attempt to request AAAA
|
||||
records at all, thereby making the IPv6 connectivity of the tunnel
|
||||
redundant. To work around this issue I created a very dodgy-looking
|
||||
script that is run post-up by wireguard-quick, which creates a custom
|
||||
network service for the tunnel interface. If you have this problem and
|
||||
want this script, please [contact me](/#contact) directly as I don't
|
||||
feel comfortable publishing something that terrible on my website.
|
||||
|
||||
Back to wireguard-quick, though. It turns out that the macOS version is
|
||||
particularly dodgy, since the bypass for the "we only have one routing
|
||||
table to work with" problem the devs went for is adding a more specific
|
||||
override route for the tunnel endpoint, which depends on parsing
|
||||
unchecked output of commands that print the routing table to determine
|
||||
the default gateway. For reasons I can't explain, this breaks when you
|
||||
create a custom network service as mentioned above, and it tries to set
|
||||
the default gateway to <span class="highlight-bg">link#32</span> or
|
||||
similar. This fails, and therefore the tunnel breaks, as tunnel traffic
|
||||
is then routed back through the tunnel in an infinite loop. After
|
||||
contacting the devs in the #wireguard channel on
|
||||
[libera.chat](https://libera.chat){target="_blank"}, my patch
|
||||
was accepted and this specific problem shouldn't occur anymore, though
|
||||
that doesn't change the bodge that is the route monitor code of
|
||||
wg-quick.
|
||||
|
||||
### Epilogue
|
||||
|
||||
That concludes the network setup. Everything is running smoothly and
|
||||
thus far, no further bugs were found. I provide IPv6-tunnels for a few
|
||||
friends who haven't reported any problems either, so at least for now I
|
||||
think that this project is complete.
|
||||
|
||||
I have already removed IPv4 addresses from a few services I run, and I
|
||||
hope to do so for the entirety of my online presence by the end of 2021,
|
||||
maybe with a few exceptions for critical services used by friends from
|
||||
Austria where major telcos still don't support IPv6.
|
||||
|
||||
Maybe the end of IPv4 is actually near, at least in my small corner of
|
||||
the internet. Thanks for reading, and have a wonderful day.
|
44
zotan.pw-web/Program.cs
Normal file
44
zotan.pw-web/Program.cs
Normal file
|
@ -0,0 +1,44 @@
|
|||
using System.Reflection;
|
||||
using LinqToDB.Data;
|
||||
using zotanpw_web;
|
||||
using zotanpw_web.database;
|
||||
|
||||
DataConnection.DefaultSettings = new Database.Settings();
|
||||
Migrations.RunMigrations();
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
// Add services to the container.
|
||||
builder.Services.AddRazorPages();
|
||||
builder.Services.AddSession(options => {
|
||||
options.IdleTimeout = TimeSpan.MaxValue;
|
||||
options.Cookie.HttpOnly = true;
|
||||
options.Cookie.IsEssential = true;
|
||||
});
|
||||
|
||||
#if (DEBUG)
|
||||
builder.Services.AddControllers().AddRazorRuntimeCompilation();
|
||||
builder.Services.AddControllers();
|
||||
#else
|
||||
builder.Services.AddControllers();
|
||||
#endif
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
// Configure the HTTP request pipeline.
|
||||
if (!app.Environment.IsDevelopment()) {
|
||||
app.UseExceptionHandler("/error");
|
||||
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
|
||||
app.UseHsts();
|
||||
}
|
||||
|
||||
app.UseStaticFiles();
|
||||
|
||||
app.UseSession();
|
||||
app.UseRouting();
|
||||
app.UseAuthorization();
|
||||
|
||||
app.MapRazorPages();
|
||||
app.MapControllers();
|
||||
|
||||
app.Run();
|
23
zotan.pw-web/Properties/launchSettings.json
Normal file
23
zotan.pw-web/Properties/launchSettings.json
Normal file
|
@ -0,0 +1,23 @@
|
|||
{
|
||||
|
||||
"profiles": {
|
||||
"http": {
|
||||
"commandName": "Project",
|
||||
"dotnetRunMessages": true,
|
||||
"launchBrowser": false,
|
||||
"applicationUrl": "http://localhost:5073",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
},
|
||||
"https": {
|
||||
"commandName": "Project",
|
||||
"dotnetRunMessages": true,
|
||||
"launchBrowser": false,
|
||||
"applicationUrl": "https://localhost:7119;http://localhost:5073",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
33
zotan.pw-web/Travelynx/Travelynx.cs
Normal file
33
zotan.pw-web/Travelynx/Travelynx.cs
Normal file
|
@ -0,0 +1,33 @@
|
|||
using LinqToDB;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using zotanpw_web.database;
|
||||
using zotanpw_web.database.Tables;
|
||||
|
||||
namespace zotanpw_web.Travelynx;
|
||||
|
||||
[ApiController]
|
||||
[Route("/travelynx")]
|
||||
public class Travelynx : Controller {
|
||||
private static readonly string TravelynxSecret = System.IO.File.ReadAllLines(".bearer_token")[0];
|
||||
|
||||
[HttpPost]
|
||||
public TravelynxInfo Update([FromBody] WebhookRequest rq) {
|
||||
var token = Request.Headers.Authorization;
|
||||
if (token == TravelynxSecret) {
|
||||
var db = new Database.DbConn();
|
||||
if (!db.TravelynxInfo.Any()) {
|
||||
db.InsertWithIdentity(new TravelynxInfo { CheckedIn = false });
|
||||
}
|
||||
|
||||
var status = db.TravelynxInfo.First();
|
||||
status.CheckedIn = rq.Status.CheckedIn;
|
||||
status.Train = $"{rq.Status.Train?.Type} {rq.Status.Train?.No}";
|
||||
status.Destination = rq.Status.ToStation?.Name;
|
||||
db.Update(status);
|
||||
return status;
|
||||
}
|
||||
|
||||
Response.StatusCode = 403;
|
||||
return null!;
|
||||
}
|
||||
}
|
43
zotan.pw-web/Travelynx/WebhookRequest.cs
Normal file
43
zotan.pw-web/Travelynx/WebhookRequest.cs
Normal file
|
@ -0,0 +1,43 @@
|
|||
namespace zotanpw_web.Travelynx {
|
||||
using System.Collections.Generic;
|
||||
|
||||
public class WebhookRequest {
|
||||
public string Reason { get; set; }
|
||||
public Status Status { get; set; }
|
||||
}
|
||||
|
||||
public class Status {
|
||||
public bool Deprecated { get; set; }
|
||||
public bool CheckedIn { get; set; }
|
||||
public Station? FromStation { get; set; }
|
||||
public Station? ToStation { get; set; }
|
||||
public List<IntermediateStop>? IntermediateStops { get; set; }
|
||||
public Train? Train { get; set; }
|
||||
public long ActionTime { get; set; }
|
||||
}
|
||||
|
||||
public class Station {
|
||||
public string? Name { get; set; }
|
||||
public string? Ds100 { get; set; }
|
||||
public long Uic { get; set; }
|
||||
public double Latitude { get; set; }
|
||||
public double Longitude { get; set; }
|
||||
public long ScheduledTime { get; set; }
|
||||
public long RealTime { get; set; }
|
||||
}
|
||||
|
||||
public class IntermediateStop {
|
||||
public string? Name { get; set; }
|
||||
public long ScheduledArrival { get; set; }
|
||||
public long RealArrival { get; set; }
|
||||
public long ScheduledDeparture { get; set; }
|
||||
public long RealDeparture { get; set; }
|
||||
}
|
||||
|
||||
public class Train {
|
||||
public string? Type { get; set; }
|
||||
public string? Line { get; set; }
|
||||
public string? No { get; set; }
|
||||
public string? Id { get; set; }
|
||||
}
|
||||
}
|
24
zotan.pw-web/Utils.cs
Normal file
24
zotan.pw-web/Utils.cs
Normal file
|
@ -0,0 +1,24 @@
|
|||
using System.Reflection;
|
||||
|
||||
namespace zotanpw_web;
|
||||
|
||||
public static class Utils {
|
||||
public static readonly string Version =
|
||||
((AssemblyInformationalVersionAttribute)Assembly.GetEntryAssembly()!.GetCustomAttributes(typeof(AssemblyInformationalVersionAttribute), false)[0])
|
||||
.InformationalVersion[6..];
|
||||
|
||||
public static readonly string LinkVersion = Version[..8];
|
||||
|
||||
// Works up to 79, doubt i'll have >=80 minute read time blog posts
|
||||
public static string a_an(int number) {
|
||||
if (number >= 80)
|
||||
throw new ArgumentOutOfRangeException();
|
||||
|
||||
return number switch {
|
||||
8 => "an",
|
||||
11 => "an",
|
||||
18 => "an",
|
||||
_ => "a"
|
||||
};
|
||||
}
|
||||
}
|
9
zotan.pw-web/appsettings.Development.json
Normal file
9
zotan.pw-web/appsettings.Development.json
Normal file
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"DetailedErrors": true,
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
}
|
||||
}
|
9
zotan.pw-web/appsettings.json
Normal file
9
zotan.pw-web/appsettings.json
Normal file
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
},
|
||||
"AllowedHosts": "*"
|
||||
}
|
34
zotan.pw-web/database/Database.cs
Normal file
34
zotan.pw-web/database/Database.cs
Normal file
|
@ -0,0 +1,34 @@
|
|||
using LinqToDB;
|
||||
using LinqToDB.Configuration;
|
||||
using LinqToDB.Data;
|
||||
using zotanpw_web.database.Tables;
|
||||
|
||||
namespace zotanpw_web.database;
|
||||
|
||||
public class Database {
|
||||
private class ConnectionStringSettings : IConnectionStringSettings {
|
||||
public string ConnectionString { get; set; } = null!;
|
||||
public string Name { get; set; } = null!;
|
||||
public string ProviderName { get; set; } = null!;
|
||||
public bool IsGlobal => false;
|
||||
}
|
||||
|
||||
public class Settings : ILinqToDBSettings {
|
||||
public IEnumerable<IDataProviderSettings> DataProviders => Enumerable.Empty<IDataProviderSettings>();
|
||||
|
||||
public string DefaultConfiguration => "SQLite";
|
||||
public string DefaultDataProvider => "SQLite";
|
||||
|
||||
public IEnumerable<IConnectionStringSettings> ConnectionStrings {
|
||||
get { yield return new ConnectionStringSettings { Name = "db", ProviderName = "SQLite", ConnectionString = @"Data Source=database.db;" }; }
|
||||
}
|
||||
}
|
||||
|
||||
public class DbConn : DataConnection {
|
||||
public DbConn() : base("db") { }
|
||||
public ITable<DbInfo> DbInfo => this.GetTable<DbInfo>();
|
||||
public ITable<TravelynxInfo> TravelynxInfo => this.GetTable<TravelynxInfo>();
|
||||
public ITable<AlbumHistoryEntry> AlbumHistory => this.GetTable<AlbumHistoryEntry>();
|
||||
public ITable<PlaylistHistoryEntry> PlaylistHistory => this.GetTable<PlaylistHistoryEntry>();
|
||||
}
|
||||
}
|
14
zotan.pw-web/database/Tables/AlbumHistory.cs
Normal file
14
zotan.pw-web/database/Tables/AlbumHistory.cs
Normal file
|
@ -0,0 +1,14 @@
|
|||
using LinqToDB.Mapping;
|
||||
#pragma warning disable CS8618
|
||||
|
||||
namespace zotanpw_web.database.Tables;
|
||||
|
||||
[Table(Name = "AlbumHistory")]
|
||||
public class AlbumHistoryEntry {
|
||||
[Column(Name = "EntryID"), PrimaryKey, Identity, NotNull] public int EntryId { get; set; }
|
||||
[Column(Name = "DateTime"), NotNull] public DateTime DateTime { get; set; }
|
||||
[Column(Name = "Artist"), NotNull] public string Artist { get; set; }
|
||||
[Column(Name = "Title"), NotNull] public string Title { get; set; }
|
||||
[Column(Name = "Source"), NotNull] public string Source { get; set; }
|
||||
[Column(Name = "Link")] public string Link { get; set; }
|
||||
}
|
9
zotan.pw-web/database/Tables/DbInfo.cs
Normal file
9
zotan.pw-web/database/Tables/DbInfo.cs
Normal file
|
@ -0,0 +1,9 @@
|
|||
using LinqToDB.Mapping;
|
||||
|
||||
namespace zotanpw_web.database.Tables;
|
||||
|
||||
[Table(Name = "DbInfo")]
|
||||
public class DbInfo {
|
||||
[Column(Name = "ID"), PrimaryKey, Identity, NotNull] public int Id { get; set; }
|
||||
[Column(Name = "DbVer"), NotNull] public int DbVer { get; set; }
|
||||
}
|
14
zotan.pw-web/database/Tables/PlaylistHistory.cs
Normal file
14
zotan.pw-web/database/Tables/PlaylistHistory.cs
Normal file
|
@ -0,0 +1,14 @@
|
|||
using LinqToDB.Mapping;
|
||||
#pragma warning disable CS8618
|
||||
|
||||
namespace zotanpw_web.database.Tables;
|
||||
|
||||
[Table(Name = "PlaylistHistory")]
|
||||
public class PlaylistHistoryEntry {
|
||||
[Column(Name = "EntryID"), PrimaryKey, Identity, NotNull] public int EntryId { get; set; }
|
||||
[Column(Name = "DateTime"), NotNull] public DateTime DateTime { get; set; }
|
||||
[Column(Name = "Author"), NotNull] public string Artist { get; set; }
|
||||
[Column(Name = "Title"), NotNull] public string Title { get; set; }
|
||||
[Column(Name = "Source"), NotNull] public string Source { get; set; }
|
||||
[Column(Name = "Link")] public string Link { get; set; }
|
||||
}
|
11
zotan.pw-web/database/Tables/TravelynxInfo.cs
Normal file
11
zotan.pw-web/database/Tables/TravelynxInfo.cs
Normal file
|
@ -0,0 +1,11 @@
|
|||
using LinqToDB.Mapping;
|
||||
|
||||
namespace zotanpw_web.database.Tables;
|
||||
|
||||
[Table(Name = "Travelynx")]
|
||||
public class TravelynxInfo {
|
||||
[Column(Name = "ID"), PrimaryKey, Identity, NotNull] public int Id { get; set; }
|
||||
[Column(Name = "CheckedIn"), NotNull] public bool CheckedIn { get; set; }
|
||||
[Column(Name = "Train")] public string? Train { get; set; }
|
||||
[Column(Name = "Destination")] public string? Destination { get; set; }
|
||||
}
|
372
zotan.pw-web/wwwroot/css/site.css
Normal file
372
zotan.pw-web/wwwroot/css/site.css
Normal file
|
@ -0,0 +1,372 @@
|
|||
/*
|
||||
generated by rouge http://rouge.jneen.net/
|
||||
original base16 by Chris Kempson (https://github.com/chriskempson/base16)
|
||||
*/
|
||||
.highlight table td {
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.highlight table pre {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.highlight, .highlight .w {
|
||||
color: #d0d0d0;
|
||||
}
|
||||
|
||||
.highlight .err {
|
||||
color: #151515;
|
||||
background-color: #ac4142;
|
||||
}
|
||||
|
||||
.highlight .c, .highlight .cd, .highlight .cm, .highlight .c1, .highlight .cs {
|
||||
color: #888;
|
||||
}
|
||||
|
||||
.highlight .cp {
|
||||
color: #f4bf75;
|
||||
}
|
||||
|
||||
.highlight .nt {
|
||||
color: #f4bf75;
|
||||
}
|
||||
|
||||
.highlight .o, .highlight .ow {
|
||||
color: #d0d0d0;
|
||||
}
|
||||
|
||||
.highlight .p, .highlight .pi {
|
||||
color: #d0d0d0;
|
||||
}
|
||||
|
||||
.highlight .gi {
|
||||
color: #90a959;
|
||||
}
|
||||
|
||||
.highlight .gd {
|
||||
color: #ac4142;
|
||||
}
|
||||
|
||||
.highlight .gh {
|
||||
color: #6a9fb5;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.highlight .k, .highlight .kn, .highlight .kp, .highlight .kr, .highlight .kv {
|
||||
color: #aa759f;
|
||||
}
|
||||
|
||||
.highlight .kc {
|
||||
color: #d28445;
|
||||
}
|
||||
|
||||
.highlight .kt {
|
||||
color: #d28445;
|
||||
}
|
||||
|
||||
.highlight .kd {
|
||||
color: #d28445;
|
||||
}
|
||||
|
||||
.highlight .s, .highlight .sb, .highlight .sc, .highlight .sd, .highlight .s2, .highlight .sh, .highlight .sx, .highlight .s1 {
|
||||
color: #90a959;
|
||||
}
|
||||
|
||||
.highlight .sr {
|
||||
color: #75b5aa;
|
||||
}
|
||||
|
||||
.highlight .si {
|
||||
color: #8f5536;
|
||||
}
|
||||
|
||||
.highlight .se {
|
||||
color: #8f5536;
|
||||
}
|
||||
|
||||
.highlight .nn {
|
||||
color: #f4bf75;
|
||||
}
|
||||
|
||||
.highlight .nc {
|
||||
color: #f4bf75;
|
||||
}
|
||||
|
||||
.highlight .no {
|
||||
color: #f4bf75;
|
||||
}
|
||||
|
||||
.highlight .na {
|
||||
color: #6a9fb5;
|
||||
}
|
||||
|
||||
.highlight .m, .highlight .mf, .highlight .mh, .highlight .mi, .highlight .il, .highlight .mo, .highlight .mb, .highlight .mx {
|
||||
color: #90a959;
|
||||
}
|
||||
|
||||
.highlight .ss {
|
||||
color: #90a959;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background: #151515 0 0;
|
||||
color: #eaeaea;
|
||||
font-size: 16px;
|
||||
line-height: 1.5;
|
||||
font-family: Monaco, "Bitstream Vera Sans Mono", "Lucida Console", Terminal, monospace;
|
||||
}
|
||||
|
||||
/* General & 'Reset' Stuff */
|
||||
.container {
|
||||
width: 90%;
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
section {
|
||||
display: block;
|
||||
margin: 20px 0 0 0;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
margin: 20px 0 0 0;
|
||||
}
|
||||
|
||||
header h2 {
|
||||
margin: 0 0 0 0 !important;
|
||||
}
|
||||
|
||||
li {
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
/* Header, <header>
|
||||
header - container
|
||||
h1 - project name
|
||||
h2 - project description
|
||||
*/
|
||||
header {
|
||||
background: rgba(0, 0, 0, 0.1);
|
||||
width: 100%;
|
||||
border-bottom: 1px dashed #b5e853;
|
||||
padding: 20px 0;
|
||||
margin: 0 0 40px 0;
|
||||
}
|
||||
|
||||
header h1 {
|
||||
font-size: 30px;
|
||||
line-height: 1.5;
|
||||
margin: 0 0 0 -40px;
|
||||
font-weight: bold;
|
||||
font-family: Monaco, "Bitstream Vera Sans Mono", "Lucida Console", Terminal, monospace;
|
||||
color: #b5e853;
|
||||
text-shadow: 0 1px 1px rgba(0, 0, 0, 0.1), 0 0 5px rgba(181, 232, 83, 0.1), 0 0 10px rgba(181, 232, 83, 0.1);
|
||||
letter-spacing: -1px;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
header h1:before {
|
||||
content: "./ ";
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
header h2 {
|
||||
font-size: 18px;
|
||||
font-weight: 300;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
#downloads .btn {
|
||||
display: inline-block;
|
||||
text-align: center;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#tor-url {
|
||||
font-size: smaller;
|
||||
}
|
||||
|
||||
/* Main Content
|
||||
*/
|
||||
#main_content {
|
||||
width: 100%;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
section img {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
font-weight: normal;
|
||||
font-family: Monaco, "Bitstream Vera Sans Mono", "Lucida Console", Terminal, monospace;
|
||||
color: #b5e853;
|
||||
letter-spacing: -0.03em;
|
||||
text-shadow: 0 1px 1px rgba(0, 0, 0, 0.1), 0 0 5px rgba(181, 232, 83, 0.1), 0 0 10px rgba(181, 232, 83, 0.1);
|
||||
}
|
||||
|
||||
#main_content h1 {
|
||||
font-size: 30px;
|
||||
}
|
||||
|
||||
#main_content h2 {
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
#main_content h3 {
|
||||
font-size: 18px;
|
||||
margin-bottom: -5px;
|
||||
}
|
||||
|
||||
#main_content h4 {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
#main_content h5 {
|
||||
font-size: 12px;
|
||||
text-transform: uppercase;
|
||||
margin: 0 0 5px 0;
|
||||
}
|
||||
|
||||
#main_content h6 {
|
||||
font-size: 12px;
|
||||
text-transform: uppercase;
|
||||
color: #999;
|
||||
margin: 0 0 5px 0;
|
||||
}
|
||||
|
||||
dt {
|
||||
font-style: italic;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
ul, p {
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
ul li {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
ul li:before {
|
||||
content: " >> ";
|
||||
font-family: Monaco, "Bitstream Vera Sans Mono", "Lucida Console", Terminal, monospace;
|
||||
font-size: 13px;
|
||||
color: #b5e853;
|
||||
margin-left: -37px;
|
||||
margin-right: 8px;
|
||||
line-height: 16px;
|
||||
}
|
||||
|
||||
blockquote {
|
||||
color: #aaa;
|
||||
padding-left: 10px;
|
||||
border-left: 1px dotted #666;
|
||||
}
|
||||
|
||||
pre {
|
||||
background: rgba(0, 0, 0, 0.9);
|
||||
border: 1px solid rgba(255, 255, 255, 0.15);
|
||||
padding: 10px;
|
||||
font-size: 16px;
|
||||
color: #b5e853;
|
||||
border-radius: 2px;
|
||||
-moz-border-radius: 2px;
|
||||
-webkit-border-radius: 2px;
|
||||
text-wrap: normal;
|
||||
overflow: auto;
|
||||
overflow-y: hidden;
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
margin: 0 0 20px 0;
|
||||
}
|
||||
|
||||
th {
|
||||
text-align: left;
|
||||
border-bottom: 1px dashed #b5e853;
|
||||
padding: 5px 10px;
|
||||
}
|
||||
|
||||
td {
|
||||
padding: 5px 10px;
|
||||
}
|
||||
|
||||
hr {
|
||||
height: 0;
|
||||
border: 0;
|
||||
border-bottom: 1px dashed #b5e853;
|
||||
color: #b5e853;
|
||||
}
|
||||
|
||||
/* Links
|
||||
a, a:hover, a:visited
|
||||
*/
|
||||
a {
|
||||
color: #63c0f5;
|
||||
text-shadow: 0 0 5px rgba(104, 182, 255, 0.3);
|
||||
text-underline-offset: 2px;
|
||||
}
|
||||
|
||||
.footerlink {
|
||||
color: #666666;
|
||||
text-shadow: 0 0 5px rgba(104, 182, 255, 0.3);
|
||||
text-underline-offset: 3px;
|
||||
text-decoration-color: rgba(102, 102, 102, 0.3);
|
||||
}
|
||||
|
||||
.footerlink:hover {
|
||||
text-decoration-color: rgba(102, 102, 102, 0.7);
|
||||
}
|
||||
|
||||
/* Clearfix */
|
||||
.cf:before, .cf:after {
|
||||
content: "";
|
||||
display: table;
|
||||
}
|
||||
|
||||
.cf:after {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.cf {
|
||||
zoom: 1;
|
||||
}
|
||||
|
||||
header h1 a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
/* Scrollbar colors */
|
||||
/* Works on Firefox */
|
||||
* {
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: grey #151515;
|
||||
}
|
||||
|
||||
/* Works on Chrome, Edge, and Safari */
|
||||
*::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
}
|
||||
|
||||
*::-webkit-scrollbar-track {
|
||||
background: #151515;
|
||||
}
|
||||
|
||||
*::-webkit-scrollbar-thumb {
|
||||
background-color: grey;
|
||||
border-radius: 20px;
|
||||
}
|
||||
|
||||
h1#post {
|
||||
margin-top: 5px;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.highlight-bg {
|
||||
background-color: #333;
|
||||
padding: 1px 4px;
|
||||
}
|
BIN
zotan.pw-web/wwwroot/files/SmartHome.pdf
Normal file
BIN
zotan.pw-web/wwwroot/files/SmartHome.pdf
Normal file
Binary file not shown.
BIN
zotan.pw-web/wwwroot/files/StreamingCrypto.pdf
Normal file
BIN
zotan.pw-web/wwwroot/files/StreamingCrypto.pdf
Normal file
Binary file not shown.
BIN
zotan.pw-web/wwwroot/files/blog/adhd-and-notes.png
Normal file
BIN
zotan.pw-web/wwwroot/files/blog/adhd-and-notes.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 365 KiB |
2421
zotan.pw-web/wwwroot/files/help.svg
Normal file
2421
zotan.pw-web/wwwroot/files/help.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 2 MiB |
38
zotan.pw-web/wwwroot/files/primary.gpg
Normal file
38
zotan.pw-web/wwwroot/files/primary.gpg
Normal file
|
@ -0,0 +1,38 @@
|
|||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
|
||||
mDMEYJLXRxYJKwYBBAHaRw8BAQdAtmSNS3C7/M6kVd7fNIJ04Lv52MCSmq02udSA
|
||||
7mncZWq0I0xhdXJhIEhhdXNtYW5uIDxsYXVyYUBoYXVzbWFubi5kZXY+iJkEExYI
|
||||
AEECGwMFCwkIBwIGFQoJCAsCBBYCAwECHgECF4ACGQEWIQT4pdrADkNRGSCJQvnQ
|
||||
ROhMW+AWBQUCYdcZigUJBP7AMAAKCRDQROhMW+AWBZEBAQD/CrEQfkaCVi1SPrEN
|
||||
5wmn/71F8fa170usNt5riNkfYwEAseybWYo+VmeE8tYa4sEBPFZoaPQAwM/wjwgf
|
||||
zSJ7kQOIdQQQFggAHRYhBMvsHgj+kApKjUJoVV+LDAswTEVxBQJgktdxAAoJEF+L
|
||||
DAswTEVxSyoBAM5EwfBuiELA4Atl7uXRVPvT1/qabFzr6Rs6FPRXXlRMAP9k78mo
|
||||
IqG0JoITc0ZgnAUtN3blbMLW0I0smrYLu5CKC4kCMwQQAQgAHRYhBPG7exToWlhT
|
||||
mi5PVl7B04/8MhMRBQJgktd+AAoJEF7B04/8MhMR0tgP/Rjigf7E6vNAJWJK6boF
|
||||
AGMk2YeH6A98wYKWVjJX6tm4LxURrTfNGlovUuq27eE6Vedp2ZfqTfjNbp08g4x9
|
||||
Kkratcw0aEr6zAf38ukZnxQZVqgUw5N4ZWdOD3j3Gdc/Oq4oSLua3egE0z/Q2hJM
|
||||
r6zQc3y34ZDdVn22XqPuBZnaTwv0NIVXYUx6D/kwP+Yp4K4PDETVy1JotnBlvAu1
|
||||
IE77DCWSd4aW0L6DBWbi9V15B7TyYpEooYtB6ayFOMoCWXKKnveNnXoD1Jy26THM
|
||||
DjiWgngcJBuqOsetwNE3Bmt5eiaBUiWOSa6cO2RjaX+KOU2jEVMFZfOIYmE89PfV
|
||||
Wso9s+K1KAF+656xTyDbl8iBYIUeM03/6/pRQiWfr0l8EVwaTtygMuXDeun2+nuv
|
||||
6dXYxZkpSjCEeispkhHnoR6dRW86cr2WBYsMdioJ5z5ftyZ6lsM8ciMJYgq0QiAf
|
||||
nDeE8hWtfhcjZLOsooEhljOtcek9cUpkP+hw3WbNkcTS9Xj/0rVEnzQh8EKzoGBG
|
||||
OZAMclMUIOsz9U0JaVSbVedlQyT1X3ovIOaZ5yRJSWZ/3edNYmD26k16Aa/HwXRS
|
||||
CbOPB52Vb67/kgcS9Cv+bfrN4kc2UynGPMNah6reSpJvGlmAHAc+0EXtBouOUAyJ
|
||||
Qz6f5t7JvqR2CjynJ8OP3jAriJkEExYIAEECGwMFCQHhM4AFCwkIBwIGFQoJCAsC
|
||||
BBYCAwECHgECF4AWIQT4pdrADkNRGSCJQvnQROhMW+AWBQUCYJLYDwIZAQAKCRDQ
|
||||
ROhMW+AWBXnMAP9rntrchSROX0jzU9magm17YcbANjDL674jKC6LkOGk5AEAicKb
|
||||
RKKPUG8pFNV2mOmgf35ogYJA3FinGxspTRy7CAy0H0xhdXJhIEhhdXNtYW5uIDx6
|
||||
b3RhbkB6b3Rhbi5wdz6IlgQTFggAPgIbAwULCQgHAgYVCgkICwIEFgIDAQIeAQIX
|
||||
gBYhBPil2sAOQ1EZIIlC+dBE6Exb4BYFBQJh1xmKBQkE/sAwAAoJENBE6Exb4BYF
|
||||
qfIBAI7yoj18pY8rSCIe0JwdsDamTu7i1tp9l601jcLWw4QPAP434R/1dh/sPL5X
|
||||
vXigfmwbP8dg/ROpXbMNbP7DZIDyBoiWBBMWCAA+FiEE+KXawA5DURkgiUL50ETo
|
||||
TFvgFgUFAmCS2AsCGwMFCQHhM4AFCwkIBwIGFQoJCAsCBBYCAwECHgECF4AACgkQ
|
||||
0EToTFvgFgWVVgEA37FMZ8jCbML8GJT5hJZFFRVu69pE+R48EdERZL10PA0BAOrS
|
||||
NVy89FoUtGZqQQXj/Gh/skzPCUWe8yoh7+Jqs8oNuDgEYJLXRxIKKwYBBAGXVQEF
|
||||
AQEHQB6hjE6145oEWIw8ZlnvJgakZj8HAQJUfFbC+pzfcKMbAwEIB4h+BBgWCAAm
|
||||
AhsMFiEE+KXawA5DURkgiUL50EToTFvgFgUFAmMwkdgFCQT+3ZoACgkQ0EToTFvg
|
||||
FgUh2gEAieyZU0y/2CxC5vks8DcX4Q4xB2G19dNNIB8sHsgDDlsBAL36dDtt/irh
|
||||
78Q59S49BEX+wx8AXInt2YOmeMQ8FsEA
|
||||
=jz+5
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
4
zotan.pw-web/wwwroot/js/site.js
Normal file
4
zotan.pw-web/wwwroot/js/site.js
Normal file
|
@ -0,0 +1,4 @@
|
|||
// Please see documentation at https://docs.microsoft.com/aspnet/core/client-side/bundling-and-minification
|
||||
// for details on configuring this project to bundle and minify static web assets.
|
||||
|
||||
// Write your JavaScript code.
|
81
zotan.pw-web/zotan.pw-web.csproj
Normal file
81
zotan.pw-web/zotan.pw-web.csproj
Normal file
|
@ -0,0 +1,81 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<RootNamespace>zotanpw_web</RootNamespace>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\css\bootstrap-grid.css" />
|
||||
<_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\css\bootstrap-grid.css.map" />
|
||||
<_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\css\bootstrap-grid.min.css" />
|
||||
<_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\css\bootstrap-grid.min.css.map" />
|
||||
<_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\css\bootstrap-grid.rtl.css" />
|
||||
<_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\css\bootstrap-grid.rtl.css.map" />
|
||||
<_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\css\bootstrap-grid.rtl.min.css" />
|
||||
<_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\css\bootstrap-grid.rtl.min.css.map" />
|
||||
<_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\css\bootstrap-reboot.css" />
|
||||
<_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\css\bootstrap-reboot.css.map" />
|
||||
<_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\css\bootstrap-reboot.min.css" />
|
||||
<_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\css\bootstrap-reboot.min.css.map" />
|
||||
<_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\css\bootstrap-reboot.rtl.css" />
|
||||
<_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\css\bootstrap-reboot.rtl.css.map" />
|
||||
<_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\css\bootstrap-reboot.rtl.min.css" />
|
||||
<_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\css\bootstrap-reboot.rtl.min.css.map" />
|
||||
<_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\css\bootstrap-utilities.css" />
|
||||
<_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\css\bootstrap-utilities.css.map" />
|
||||
<_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\css\bootstrap-utilities.min.css" />
|
||||
<_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\css\bootstrap-utilities.min.css.map" />
|
||||
<_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\css\bootstrap-utilities.rtl.css" />
|
||||
<_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\css\bootstrap-utilities.rtl.css.map" />
|
||||
<_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\css\bootstrap-utilities.rtl.min.css" />
|
||||
<_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\css\bootstrap-utilities.rtl.min.css.map" />
|
||||
<_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\css\bootstrap.css" />
|
||||
<_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\css\bootstrap.css.map" />
|
||||
<_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\css\bootstrap.min.css" />
|
||||
<_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\css\bootstrap.min.css.map" />
|
||||
<_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\css\bootstrap.rtl.css" />
|
||||
<_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\css\bootstrap.rtl.css.map" />
|
||||
<_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\css\bootstrap.rtl.min.css" />
|
||||
<_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\css\bootstrap.rtl.min.css.map" />
|
||||
<_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\js\bootstrap.bundle.js" />
|
||||
<_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\js\bootstrap.bundle.js.map" />
|
||||
<_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\js\bootstrap.bundle.min.js" />
|
||||
<_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\js\bootstrap.bundle.min.js.map" />
|
||||
<_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\js\bootstrap.esm.js" />
|
||||
<_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\js\bootstrap.esm.js.map" />
|
||||
<_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\js\bootstrap.esm.min.js" />
|
||||
<_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\js\bootstrap.esm.min.js.map" />
|
||||
<_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\js\bootstrap.js" />
|
||||
<_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\js\bootstrap.js.map" />
|
||||
<_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\js\bootstrap.min.js" />
|
||||
<_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\js\bootstrap.min.js.map" />
|
||||
<_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\LICENSE" />
|
||||
<_ContentIncludedByDefault Remove="wwwroot\lib\jquery-validation\dist\additional-methods.js" />
|
||||
<_ContentIncludedByDefault Remove="wwwroot\lib\jquery-validation\dist\additional-methods.min.js" />
|
||||
<_ContentIncludedByDefault Remove="wwwroot\lib\jquery-validation\dist\jquery.validate.js" />
|
||||
<_ContentIncludedByDefault Remove="wwwroot\lib\jquery-validation\dist\jquery.validate.min.js" />
|
||||
<_ContentIncludedByDefault Remove="wwwroot\lib\jquery-validation\LICENSE.md" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="linq2db" Version="4.3.0" />
|
||||
<PackageReference Include="Markdig" Version="0.30.4" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" Version="7.0.0" />
|
||||
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="7.0.0" />
|
||||
<PackageReference Include="System.Data.SQLite" Version="1.0.115" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="Pages\blog\posts" />
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="SetSourceRevisionId" BeforeTargets="InitializeSourceControlInformation">
|
||||
<Exec Command="git describe --long --always --dirty --exclude=* --abbrev=8" ConsoleToMSBuild="True" IgnoreExitCode="False">
|
||||
<Output PropertyName="SourceRevisionId" TaskParameter="ConsoleOutput" />
|
||||
</Exec>
|
||||
</Target>
|
||||
|
||||
</Project>
|
Loading…
Reference in a new issue