commit 7a9d910d86980884d65847723160e3eb11ccb44f Author: Laura Hausmann Date: Sun Nov 20 03:06:41 2022 +0100 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b6b7c26 --- /dev/null +++ b/.gitignore @@ -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 diff --git a/zotan.pw-web.sln b/zotan.pw-web.sln new file mode 100644 index 0000000..387c260 --- /dev/null +++ b/zotan.pw-web.sln @@ -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 diff --git a/zotan.pw-web/Migrations.cs b/zotan.pw-web/Migrations.cs new file mode 100644 index 0000000..183f91a --- /dev/null +++ b/zotan.pw-web/Migrations.cs @@ -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 _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(); + db.CreateTable(); + db.CreateTable(); + db.CreateTable(); + + db.InsertWithIdentity(new DbInfo { DbVer = DbVer }); + } + else if (db.DataProvider.GetSchemaProvider().GetSchema(db).Tables.All(t => t.TableName != "DbInfo")) { + db.CreateTable(); + 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); + } + } +} diff --git a/zotan.pw-web/Pages/Error.cshtml b/zotan.pw-web/Pages/Error.cshtml new file mode 100644 index 0000000..090dc52 --- /dev/null +++ b/zotan.pw-web/Pages/Error.cshtml @@ -0,0 +1,25 @@ +@page +@model ErrorModel +@{ + ViewData["Title"] = "error"; +} + +

Error.

+

An error occurred while processing your request.

+ +@if (Model.ShowRequestId) { +

+ Request ID: @Model.RequestId +

+} + +

Development Mode

+

+ Swapping to the Development environment displays detailed information about the error that occurred. +

+

+ The Development environment shouldn't be enabled for deployed applications. + It can result in displaying sensitive information from exceptions to end users. + For local debugging, enable the Development environment by setting the ASPNETCORE_ENVIRONMENT environment variable to Development + and restarting the app. +

diff --git a/zotan.pw-web/Pages/Error.cshtml.cs b/zotan.pw-web/Pages/Error.cshtml.cs new file mode 100644 index 0000000..f405c06 --- /dev/null +++ b/zotan.pw-web/Pages/Error.cshtml.cs @@ -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 _logger; + + public ErrorModel(ILogger logger) { + _logger = logger; + } + + public void OnGet() { + RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier; + } +} \ No newline at end of file diff --git a/zotan.pw-web/Pages/Index.cshtml b/zotan.pw-web/Pages/Index.cshtml new file mode 100644 index 0000000..2125d23 --- /dev/null +++ b/zotan.pw-web/Pages/Index.cshtml @@ -0,0 +1,162 @@ +@page +@using zotanpw_web.database +@model IndexModel +@{ + ViewData["title"] = "home"; + var travelynx = new Database.DbConn().TravelynxInfo.ToList(); +} + +@section head +{ +

Welcome internet user.

+ An experimental http3 + v6o version of this page is available here. +} + +

About me

+

Hey there, I'm Laura (~zotan, she/they), and I'm a queer, anarchist & antifascist network engineer, system administrator and software developer.

+

+ 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)) { + Most recently, I was seen on an unknon train somewhere in Germany. + } + else if (string.IsNullOrWhiteSpace(status.Destination)) { + Most recently, I was seen on @status.Train. + } + else { + Most recently, I was seen on @status.Train on the way to @status.Destination. + } + } + else { + if (string.IsNullOrWhiteSpace(status.Destination)) { + Most recently, I was seen at an unknon train station in Germany. + } + else { + Most recently, I was seen at @status.Destination. + } + } + } +

+

Here you can find my contact info, crypto keys, projects, and a few links.

+ +@if (Request.Headers["X-Forwarded-For"] == "::1") { +

Many of my services are IPv6-only, and more are soon to follow. Your browser preferred IPv4 while connecting to this website, so please check your connection for IPv6 support before contacting me if your browser displays a network error.

+} + +

Contact

+

You can contact me via the following services:

+ + +

Profiles

+ + + + + +

Photography

+ +

Projects - Networking

+
    +
  • AS211579 ~ zotan experimental networks
  • +
+

Projects - Webservices

+
    +
  • chaos.stream - a chaos-community centric live streaming platform
  • +
  • c3stream.de  - a media.ccc.de mirror with added functionality
  • +
  • ip6.biz      - IPv6 (and IPv4) address info and connection test
  • +
+ +

Projects - Software

+
    +
  • nginx-mod-rtmp - fork of the original with fixed bugs and added functionality
  • +
  • RTMPdash - the software powering chaos.stream
  • +
  • ip6.biz  - the source code for the IPv6 toolbox mentioned above
  • +
  • monithor - alerting and status page generator for InfluxDB
  • +
  • webmusic - a simple web player for your music
  • +
  • trainav  - experimental train journey planner
  • +
  • repomgr  - experimental AUR buildserver and repo manager
  • +
  • autotag  - experimental music tag normalizer
  • +
  • mediamanager - experimental media tracker
  • +
+

Projects - Hardware

+ +

Papers

+
    +
  • 2019 - Comparison of music streaming services’ encryption concepts Download
  • +
  • 2020 - Programming a modular Smart-Home-System (prescientific paper) Download
  • +
+

Crypto

+

I use the following GPG keys:

+
    +
  • Primary (ECC): F8A5 DAC0 0E43 5119 2089 42F9 D044 E84C 5BE0 1605 Pubkey
  • +
+ +@section postfooter { + +} diff --git a/zotan.pw-web/Pages/Index.cshtml.cs b/zotan.pw-web/Pages/Index.cshtml.cs new file mode 100644 index 0000000..f5dff1b --- /dev/null +++ b/zotan.pw-web/Pages/Index.cshtml.cs @@ -0,0 +1,14 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; + +namespace zotanpw_web.Pages; + +public class IndexModel : PageModel { + private readonly ILogger _logger; + + public IndexModel(ILogger logger) { + _logger = logger; + } + + public void OnGet() { } +} \ No newline at end of file diff --git a/zotan.pw-web/Pages/NowPlaying.cshtml b/zotan.pw-web/Pages/NowPlaying.cshtml new file mode 100644 index 0000000..6661a85 --- /dev/null +++ b/zotan.pw-web/Pages/NowPlaying.cshtml @@ -0,0 +1,33 @@ +@page "/np" +@model NowPlayingModel + +@{ + ViewData["title"] = "now playing"; +} + +

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.

+

Links to my music library / webplayer are accessible to authorized users only. If you think this includes you, but something isn't working, DM me.

+
    +
  • Albums
  • +
+

>> Albums

+ + + + + + + + + + + + + + + + + + + +
ArtistAlbumLink
RöyksoppProfound Mysteriesmusic.zotan.services
t+pazoliteRefactoring TravelApple Music
testtesttest
diff --git a/zotan.pw-web/Pages/NowPlaying.cshtml.cs b/zotan.pw-web/Pages/NowPlaying.cshtml.cs new file mode 100644 index 0000000..a00f342 --- /dev/null +++ b/zotan.pw-web/Pages/NowPlaying.cshtml.cs @@ -0,0 +1,9 @@ +using Microsoft.AspNetCore.Mvc.RazorPages; + +namespace zotanpw_web.Pages; + +public class NowPlayingModel : PageModel { + public void OnGet() { + + } +} diff --git a/zotan.pw-web/Pages/Shared/_Layout.cshtml b/zotan.pw-web/Pages/Shared/_Layout.cshtml new file mode 100644 index 0000000..607293d --- /dev/null +++ b/zotan.pw-web/Pages/Shared/_Layout.cshtml @@ -0,0 +1,36 @@ + + + + + + + + + zotan.pw >> @ViewData["title"] + + + +
+ +
+
+
+ @RenderBody() +
+
+ +
+ @await RenderSectionAsync("prefooter", required: false) +

--- Served by @Environment.MachineName running zotan.pw-web @Utils.Version on .NET @Environment.Version ---

+ @await RenderSectionAsync("postfooter", required: false) +
+ + + diff --git a/zotan.pw-web/Pages/_ViewImports.cshtml b/zotan.pw-web/Pages/_ViewImports.cshtml new file mode 100644 index 0000000..7bdc882 --- /dev/null +++ b/zotan.pw-web/Pages/_ViewImports.cshtml @@ -0,0 +1,3 @@ +@using zotanpw_web +@namespace zotanpw_web.Pages +@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers diff --git a/zotan.pw-web/Pages/_ViewStart.cshtml b/zotan.pw-web/Pages/_ViewStart.cshtml new file mode 100644 index 0000000..817a913 --- /dev/null +++ b/zotan.pw-web/Pages/_ViewStart.cshtml @@ -0,0 +1,3 @@ +@{ + Layout = "_Layout"; +} \ No newline at end of file diff --git a/zotan.pw-web/Pages/blog/Blog.cshtml b/zotan.pw-web/Pages/blog/Blog.cshtml new file mode 100644 index 0000000..1740832 --- /dev/null +++ b/zotan.pw-web/Pages/blog/Blog.cshtml @@ -0,0 +1,14 @@ +@page "/blog" +@model BlogModel +@{ + ViewData["title"] = "blog"; +} + +

Hey there, welcome to the blog of a disabled neurodivergent queer person unhappy with the state of the world.

+

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.

+

Posts

+
    + @foreach (var post in BlogModel.Posts) { +
  • @post.PublishedOn.ToString("yyyy-MM-dd") @post.Title
  • + } +
diff --git a/zotan.pw-web/Pages/blog/Blog.cshtml.cs b/zotan.pw-web/Pages/blog/Blog.cshtml.cs new file mode 100644 index 0000000..4026b06 --- /dev/null +++ b/zotan.pw-web/Pages/blog/Blog.cshtml.cs @@ -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 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; + } + } +} diff --git a/zotan.pw-web/Pages/blog/BlogPost.cshtml b/zotan.pw-web/Pages/blog/BlogPost.cshtml new file mode 100644 index 0000000..97c7d4c --- /dev/null +++ b/zotan.pw-web/Pages/blog/BlogPost.cshtml @@ -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 +} + +@post.PublishedOn.ToString("yyyy-MM-dd") - @Utils.a_an(post.ReadTimeMinutes) @post.ReadTimeMinutes minute read (150 wpm) +

IPv6-native networking: a project report

+
+ @Html.Raw(post.Content) +
diff --git a/zotan.pw-web/Pages/blog/posts/adhd-and-notes.md b/zotan.pw-web/Pages/blog/posts/adhd-and-notes.md new file mode 100644 index 0000000..4b72be5 --- /dev/null +++ b/zotan.pw-web/Pages/blog/posts/adhd-and-notes.md @@ -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! diff --git a/zotan.pw-web/Pages/blog/posts/ipv6-networking.md b/zotan.pw-web/Pages/blog/posts/ipv6-networking.md new file mode 100644 index 0000000..28eeb69 --- /dev/null +++ b/zotan.pw-web/Pages/blog/posts/ipv6-networking.md @@ -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, 64:ff9b::/96). 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. github.com) to +an address we can connect to (e.g. 140.82.121.3). 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 +unbound, the address 140.82.121.3 (from the real A record) is +translated to 64:ff9b::140.82.121.3, +or 64:ff9b::8c52:7903 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 somedomain.tld, the software tries to +connect to 192.0.2.255, 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 +100.64.0.0/10 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. ::1 +(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 link#32 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. \ No newline at end of file diff --git a/zotan.pw-web/Program.cs b/zotan.pw-web/Program.cs new file mode 100644 index 0000000..faaa63c --- /dev/null +++ b/zotan.pw-web/Program.cs @@ -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(); diff --git a/zotan.pw-web/Properties/launchSettings.json b/zotan.pw-web/Properties/launchSettings.json new file mode 100644 index 0000000..d70b83b --- /dev/null +++ b/zotan.pw-web/Properties/launchSettings.json @@ -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" + } + } + } +} diff --git a/zotan.pw-web/Travelynx/Travelynx.cs b/zotan.pw-web/Travelynx/Travelynx.cs new file mode 100644 index 0000000..7b5657c --- /dev/null +++ b/zotan.pw-web/Travelynx/Travelynx.cs @@ -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!; + } +} diff --git a/zotan.pw-web/Travelynx/WebhookRequest.cs b/zotan.pw-web/Travelynx/WebhookRequest.cs new file mode 100644 index 0000000..6828207 --- /dev/null +++ b/zotan.pw-web/Travelynx/WebhookRequest.cs @@ -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? 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; } + } +} diff --git a/zotan.pw-web/Utils.cs b/zotan.pw-web/Utils.cs new file mode 100644 index 0000000..b77a697 --- /dev/null +++ b/zotan.pw-web/Utils.cs @@ -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" + }; + } +} diff --git a/zotan.pw-web/appsettings.Development.json b/zotan.pw-web/appsettings.Development.json new file mode 100644 index 0000000..770d3e9 --- /dev/null +++ b/zotan.pw-web/appsettings.Development.json @@ -0,0 +1,9 @@ +{ + "DetailedErrors": true, + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/zotan.pw-web/appsettings.json b/zotan.pw-web/appsettings.json new file mode 100644 index 0000000..10f68b8 --- /dev/null +++ b/zotan.pw-web/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} diff --git a/zotan.pw-web/database/Database.cs b/zotan.pw-web/database/Database.cs new file mode 100644 index 0000000..73a4769 --- /dev/null +++ b/zotan.pw-web/database/Database.cs @@ -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 DataProviders => Enumerable.Empty(); + + public string DefaultConfiguration => "SQLite"; + public string DefaultDataProvider => "SQLite"; + + public IEnumerable 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 => this.GetTable(); + public ITable TravelynxInfo => this.GetTable(); + public ITable AlbumHistory => this.GetTable(); + public ITable PlaylistHistory => this.GetTable(); + } +} diff --git a/zotan.pw-web/database/Tables/AlbumHistory.cs b/zotan.pw-web/database/Tables/AlbumHistory.cs new file mode 100644 index 0000000..02da010 --- /dev/null +++ b/zotan.pw-web/database/Tables/AlbumHistory.cs @@ -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; } +} diff --git a/zotan.pw-web/database/Tables/DbInfo.cs b/zotan.pw-web/database/Tables/DbInfo.cs new file mode 100644 index 0000000..2c2e60e --- /dev/null +++ b/zotan.pw-web/database/Tables/DbInfo.cs @@ -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; } +} diff --git a/zotan.pw-web/database/Tables/PlaylistHistory.cs b/zotan.pw-web/database/Tables/PlaylistHistory.cs new file mode 100644 index 0000000..da37741 --- /dev/null +++ b/zotan.pw-web/database/Tables/PlaylistHistory.cs @@ -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; } +} diff --git a/zotan.pw-web/database/Tables/TravelynxInfo.cs b/zotan.pw-web/database/Tables/TravelynxInfo.cs new file mode 100644 index 0000000..83695c4 --- /dev/null +++ b/zotan.pw-web/database/Tables/TravelynxInfo.cs @@ -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; } +} diff --git a/zotan.pw-web/wwwroot/css/site.css b/zotan.pw-web/wwwroot/css/site.css new file mode 100644 index 0000000..ca67370 --- /dev/null +++ b/zotan.pw-web/wwwroot/css/site.css @@ -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 - 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; +} diff --git a/zotan.pw-web/wwwroot/files/SmartHome.pdf b/zotan.pw-web/wwwroot/files/SmartHome.pdf new file mode 100644 index 0000000..85af5df Binary files /dev/null and b/zotan.pw-web/wwwroot/files/SmartHome.pdf differ diff --git a/zotan.pw-web/wwwroot/files/StreamingCrypto.pdf b/zotan.pw-web/wwwroot/files/StreamingCrypto.pdf new file mode 100644 index 0000000..7689d67 Binary files /dev/null and b/zotan.pw-web/wwwroot/files/StreamingCrypto.pdf differ diff --git a/zotan.pw-web/wwwroot/files/blog/adhd-and-notes.png b/zotan.pw-web/wwwroot/files/blog/adhd-and-notes.png new file mode 100644 index 0000000..355c010 Binary files /dev/null and b/zotan.pw-web/wwwroot/files/blog/adhd-and-notes.png differ diff --git a/zotan.pw-web/wwwroot/files/help.svg b/zotan.pw-web/wwwroot/files/help.svg new file mode 100644 index 0000000..10067af --- /dev/null +++ b/zotan.pw-web/wwwroot/files/help.svg @@ -0,0 +1,2421 @@ + + + + + +image/svg+xml + + + + +fr2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/zotan.pw-web/wwwroot/files/primary.gpg b/zotan.pw-web/wwwroot/files/primary.gpg new file mode 100644 index 0000000..c5e12d6 --- /dev/null +++ b/zotan.pw-web/wwwroot/files/primary.gpg @@ -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----- diff --git a/zotan.pw-web/wwwroot/js/site.js b/zotan.pw-web/wwwroot/js/site.js new file mode 100644 index 0000000..ac49c18 --- /dev/null +++ b/zotan.pw-web/wwwroot/js/site.js @@ -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. diff --git a/zotan.pw-web/zotan.pw-web.csproj b/zotan.pw-web/zotan.pw-web.csproj new file mode 100644 index 0000000..50dabd6 --- /dev/null +++ b/zotan.pw-web/zotan.pw-web.csproj @@ -0,0 +1,81 @@ + + + + net7.0 + enable + enable + zotanpw_web + + + + <_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" /> + + + + + + + + + + + + + + + + + + + + +