Compare commits
3 commits
0da892b0e4
...
762377f325
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
762377f325 | ||
|
|
c657df92d9 | ||
|
|
38588715d8 |
1216
Cargo.lock
generated
27
Cargo.toml
|
|
@ -1,17 +1,21 @@
|
|||
[package]
|
||||
name = "server"
|
||||
version = "0.1.0"
|
||||
name = "mdws"
|
||||
version = "0.1.3"
|
||||
edition = "2024"
|
||||
authors = ["Penelope Gwen <support@pogmom.me>"]
|
||||
license-file = "LICENSE.md"
|
||||
description = "a markdown-based webserver built with rust and estrogen"
|
||||
|
||||
[[bin]]
|
||||
name = "mdws"
|
||||
path = "src/main.rs"
|
||||
|
||||
[dependencies]
|
||||
chrono = "0.4.43"
|
||||
markdown = "1.0.0"
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
warp = { version = "0.4", features = ["server"] }
|
||||
termimad = "0.34.1"
|
||||
text-template = "0.1.0"
|
||||
lipgloss = "0.1.1"
|
||||
mdriver = "0.14.0"
|
||||
mq-view = "0.1.9"
|
||||
mq-markdown = { version = "0.5.14", features = ["color"] }
|
||||
toml = "1.0.1"
|
||||
|
|
@ -21,6 +25,15 @@ toml-frontmatter = "0.1.0"
|
|||
serde = "1.0.228"
|
||||
rand = "0.10.0"
|
||||
strip-ansi-escapes = "0.2.1"
|
||||
dir_walker = "0.1.9"
|
||||
walkdir = "2.5.0"
|
||||
ptree = "0.5.2"
|
||||
dirs = "6.0.0"
|
||||
|
||||
[package.metadata.deb]
|
||||
changelog = "debian/changelog"
|
||||
maintainer-scripts = "debian/"
|
||||
assets = [
|
||||
["target/release/mdws", "usr/bin/", "755"],
|
||||
["config.toml", "/etc/mdws/config.toml", "644" ]
|
||||
]
|
||||
systemd-units = { enable = true }
|
||||
|
||||
|
|
|
|||
60
LICENSE.md
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
# 🏳️🌈 Opinionated Queer License v1.3
|
||||
|
||||
© Copyright {Licensor}
|
||||
|
||||
## Permissions
|
||||
|
||||
The creators of this Work (“The Licensor”) grant permission
|
||||
to any person, group or legal entity that doesn't violate the prohibitions below (“The User”),
|
||||
to do everything with this Work that would otherwise infringe their copyright or any patent claims,
|
||||
subject to the following conditions:
|
||||
|
||||
## Obligations
|
||||
|
||||
The User must give appropriate credit to the Licensor,
|
||||
provide a copy of this license or a (clickable, if the medium allows) link to
|
||||
[oql.avris.it/license/v1.3](https://oql.avris.it/license/v1.3),
|
||||
and indicate whether and what kind of changes were made.
|
||||
The User may do so in any reasonable manner,
|
||||
but not in any way that suggests the Licensor endorses the User or their use.
|
||||
|
||||
## Prohibitions
|
||||
|
||||
No one may use this Work for prejudiced or bigoted purposes, including but not limited to:
|
||||
racism, xenophobia, queerphobia, queer exclusionism, homophobia, transphobia, enbyphobia, misogyny.
|
||||
|
||||
No one may use this Work to inflict or facilitate violence or abuse of human rights,
|
||||
as defined in either of the following documents:
|
||||
[Universal Declaration of Human Rights](https://www.un.org/en/about-us/universal-declaration-of-human-rights),
|
||||
[European Convention on Human Rights](https://prd-echr.coe.int/web/echr/european-convention-on-human-rights)
|
||||
along with the rulings of the [European Court of Human Rights](https://www.echr.coe.int/).
|
||||
|
||||
No entity that commits such abuses or materially supports entities that do
|
||||
may use the Work for any reason.
|
||||
|
||||
No law enforcement, carceral institutions, immigration enforcement entities, military entities or military contractors
|
||||
may use the Work for any reason. This also applies to any individuals employed by those entities.
|
||||
|
||||
No business entity where the ratio of pay (salaried, freelance, stocks, or other benefits)
|
||||
between the highest and lowest individual in the entity is greater than 50 : 1
|
||||
may use the Work for any reason.
|
||||
|
||||
No private business run for profit with more than a thousand employees
|
||||
may use the Work for any reason.
|
||||
|
||||
Unless the User has made substantial changes to the Work,
|
||||
or uses it only as a part of a new work (eg. as a library, as a part of an anthology, etc.),
|
||||
they are prohibited from selling the Work.
|
||||
That prohibition includes processing the Work with machine learning models.
|
||||
|
||||
## Sanctions
|
||||
|
||||
If the Licensor notifies the User that they have not complied with the rules of the license,
|
||||
they can keep their license by complying within 30 days after the notice.
|
||||
If they do not do so, their license ends immediately.
|
||||
|
||||
## Warranty
|
||||
|
||||
This Work is provided “as is”, without warranty of any kind, express or implied.
|
||||
The Licensor will not be liable to anyone for any damages related to the Work or this license,
|
||||
under any kind of legal claim as far as the law allows.
|
||||
4
config.toml
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
server_root = "/srv/mdws/root"
|
||||
listen_port = 3030
|
||||
bind_address = [127, 0, 0, 1]
|
||||
server_domain = "127.0.0.1:3030"
|
||||
23
debian/changelog
vendored
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
mdws 0.1.3-1 semistable; urgency=medium
|
||||
|
||||
* i should test before pushing updates
|
||||
|
||||
-- Penelope Gwen <support@pogmom.me> Tue, 10 Mar 2026 21:09:42 -0700
|
||||
|
||||
mdws 0.1.2-1 semistable; urgency=medium
|
||||
|
||||
* fix another good
|
||||
|
||||
-- Penelope Gwen <support@pogmom.me> Tue, 10 Mar 2026 21:06:23 -0700
|
||||
|
||||
mdws 0.1.1-1 semistable; urgency=medium
|
||||
|
||||
* fix ua and page visit stuff
|
||||
|
||||
-- Penelope Gwen <support@pogmom.me> Tue, 10 Mar 2026 20:58:43 -0700
|
||||
|
||||
mdws 0.1.0-1 semistable; urgency=medium
|
||||
|
||||
* Initial release
|
||||
|
||||
-- Penelope Gwen <support@pogmom.me> Tue, 10 Mar 2026 20:09:05 -0700
|
||||
8
debian/service
vendored
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
[Unit]
|
||||
Description=Markdown Webserver
|
||||
|
||||
[Service]
|
||||
ExecStart=/usr/bin/mdws
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
|
|
@ -1,111 +0,0 @@
|
|||
@font-face {
|
||||
font-family: OverpassNerd;
|
||||
src: url("/assets/fonts/overpass/OverpassNerdFont-Regular.otf") format("opentype");
|
||||
}
|
||||
|
||||
:root {
|
||||
--main-bg-color: #111317;
|
||||
--main-fg-color: #d7afaf;
|
||||
--theme-white: #e4e4e4;
|
||||
--secondary-bg-color: color-mix(in srgb, var(--main-bg-color) 35%, var(--main-fg-color) 65%);
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: rgba(from var(--main-bg-color) r g b / 1);
|
||||
}
|
||||
|
||||
h1,
|
||||
h2,
|
||||
h3 {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
p,
|
||||
a {
|
||||
font-family: 'OverpassNerd', sans-serif;
|
||||
color: rgba(from var(--main-fg-color) r g b / 1);
|
||||
}
|
||||
|
||||
.body {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
flex-wrap: wrap-reverse;
|
||||
/* max-width: 80vmax;
|
||||
margin: auto;*/
|
||||
gap: 2vw;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
min-width: 30%;
|
||||
max-width: 400px;
|
||||
flex-grow: 1;
|
||||
/* background-color: rgba(255,0,0,0.3);*/
|
||||
}
|
||||
|
||||
.content {
|
||||
/* background-color: rgba(0,255,0,0.3);*/
|
||||
max-width: 60vmax;
|
||||
}
|
||||
|
||||
#buttons {
|
||||
img {
|
||||
display: block;
|
||||
margin: auto;
|
||||
/* width: 60%;*/
|
||||
width: 12em;
|
||||
aspect-ratio: 88 / 31;
|
||||
}
|
||||
|
||||
h1 {
|
||||
padding-top: 1vh;
|
||||
}
|
||||
|
||||
h2 {
|
||||
border-top: 1px dotted;
|
||||
padding-top: 1vh;
|
||||
}
|
||||
|
||||
p {
|
||||
text-align: center;
|
||||
font-size: 1.1em;
|
||||
}
|
||||
}
|
||||
|
||||
#sitemap {
|
||||
p {
|
||||
line-height: 0.25;
|
||||
}
|
||||
}
|
||||
|
||||
#rats {
|
||||
img {
|
||||
display: block;
|
||||
margin: auto;
|
||||
width: 90%;
|
||||
}
|
||||
|
||||
p {
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
img {
|
||||
width: 10vmax;
|
||||
}
|
||||
|
||||
.markdown-module {
|
||||
padding: 1vmax 2vmax;
|
||||
margin: 1vmax 0;
|
||||
background-color: rgba(from var(--secondary-bg-color) r g b / 0.2);
|
||||
border-radius: 8px;
|
||||
|
||||
.timestamp {
|
||||
opacity: 0.5;
|
||||
font-style: italic;
|
||||
line-height: 0.5;
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 658 B |
|
Before Width: | Height: | Size: 516 B |
|
Before Width: | Height: | Size: 398 B |
|
Before Width: | Height: | Size: 398 B |
|
Before Width: | Height: | Size: 980 B |
|
Before Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 1,000 B |
|
Before Width: | Height: | Size: 4.3 KiB |
|
Before Width: | Height: | Size: 1 KiB |
|
Before Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 7.7 KiB |
|
Before Width: | Height: | Size: 1,012 B |
|
Before Width: | Height: | Size: 8.8 KiB |
|
Before Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 783 KiB |
|
Before Width: | Height: | Size: 796 KiB |
|
Before Width: | Height: | Size: 722 KiB |
|
Before Width: | Height: | Size: 443 KiB |
|
Before Width: | Height: | Size: 2.6 MiB |
|
Before Width: | Height: | Size: 2.3 MiB |
|
Before Width: | Height: | Size: 852 KiB |
|
Before Width: | Height: | Size: 1.7 MiB |
|
Before Width: | Height: | Size: 456 KiB |
|
Before Width: | Height: | Size: 1.4 MiB |
|
Before Width: | Height: | Size: 524 KiB |
|
Before Width: | Height: | Size: 504 KiB |
|
Before Width: | Height: | Size: 541 KiB |
|
Before Width: | Height: | Size: 2.1 MiB |
|
Before Width: | Height: | Size: 519 KiB |
|
Before Width: | Height: | Size: 528 KiB |
|
Before Width: | Height: | Size: 609 KiB |
|
Before Width: | Height: | Size: 748 KiB |
|
Before Width: | Height: | Size: 425 KiB |
|
|
@ -1,36 +0,0 @@
|
|||
---toml
|
||||
title = "buttons"
|
||||
date_created = "2026-02-14"
|
||||
index = 2
|
||||
---
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
# Check out my friends!
|
||||
|
||||
## [TerminalLesbian](https://hotn.gay)
|
||||
|
||||
[][2]
|
||||
|
||||
## [Cassie Candles](https://cassiecandles.net)
|
||||
|
||||
[][3]
|
||||
|
||||
# Webrings
|
||||
|
||||
## [No AI webring](https://baccyflap.com/noai)
|
||||
|
||||
[<](https://baccyflap.com/noai/?prv&s=mom) [random](https://baccyflap.com/noai/?rnd) [>](https://baccyflap.com/noai/?nxt&s=mom)
|
||||
|
||||
## [Fediring](https://fediring.net)
|
||||
|
||||
[<](https://fediring.net/previous?host=pogmom.me) [random](https://fediring.net/rand) [>](https://fediring.net/next?host=pogmom.me)
|
||||
|
||||
## [Geekring](http://geekring.net)
|
||||
|
||||
[<](http://geekring.net/site/473/previous) [random](http://geekring.net/site/473/random) [>](http://geekring.net/site/473/next)
|
||||
|
||||
[2]:https://hotn.gay
|
||||
[3]:https://cassiecandles.net
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
---toml
|
||||
title = "rats"
|
||||
date_created = "2026-02-15"
|
||||
index = 1
|
||||
---
|
||||
|
||||

|
||||
|
||||
Please enjoy a picture of my sons :)
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
---toml
|
||||
title = "sitemap"
|
||||
index = 0
|
||||
---
|
||||
|
||||
# Sitemap
|
||||
|
||||
${sitemap}
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>${title}</title>
|
||||
<link rel="stylesheet" type="text/css" href="/assets/css/style.css">
|
||||
</head>
|
||||
<body>
|
||||
<!-- ${time}-->
|
||||
<div class="header" id="header">
|
||||
${header}
|
||||
</div>
|
||||
<div class="body" id="body">
|
||||
<div class="sidebar" id="sidebar">
|
||||
${sidebar}
|
||||
</div>
|
||||
<div class="content" id="content">
|
||||
${content}
|
||||
</div>
|
||||
</div>
|
||||
<div class="footer" id="footer">
|
||||
${footer}
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>${title}</title>
|
||||
<link rel="stylesheet" type="text/css" href="/assets/css/style.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="header" id="header">
|
||||
${header}
|
||||
</div>
|
||||
<div class="body" id="body">
|
||||
<div class="sidebar" id="sidebar">
|
||||
${sidebar}
|
||||
</div>
|
||||
<div class="content" id="content">
|
||||
${content}
|
||||
</div>
|
||||
</div>
|
||||
<div class="footer" id="footer">
|
||||
${footer}
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
Before Width: | Height: | Size: 399 KiB |
|
|
@ -1,7 +0,0 @@
|
|||
---toml
|
||||
title = "Home Page"
|
||||
date_created = "2026-01-15"
|
||||
---
|
||||
# Penelope Gwen
|
||||
## it/its, she/her
|
||||
### Technologist & Sociologist
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
---toml
|
||||
title = "home"
|
||||
index = 0
|
||||
---
|
||||
|
||||
I'm Penelope/pogmommy, a queer rat mom with a background in sociology, software development, and network engineering.
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
---toml
|
||||
title = "MusicBrainz Artist Monitoring"
|
||||
date_created = "2026-02-15"
|
||||
index = 3
|
||||
---
|
||||
# MusicBrainz Artist Monitoring
|
||||
|
||||
I do my best to honor the importance of the art and artists in my life by documenting their work on MusicBrainz, an open music encyclopedia.
|
||||
|
||||
To see the statuses of my actively-monitored artists, please see the public spreadsheet [here](https://cloud.pogmom.me/s/b8CSpAPNs8GwtWn).
|
||||
|
||||
If you would like me to consider adding an artist to the above spreadsheet, please fill out the form [here](https://cloud.pogmom.me/apps/forms/s/qzFg5kGbtqzXxHjMwS6xpPH9).
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
---toml
|
||||
title = "Archives, Feudalism, and Digital Literacy"
|
||||
date_created = "2023-09-12"
|
||||
date_updated = "2024-05-03"
|
||||
---
|
||||
|
||||
# Archives, Feudalism, and Digital Literacy
|
||||
|
||||
My undergraduate thesis synthesized my sociological studies with my aptitude for technology. Inspired by my work with my mentor, I spent the final year of my studies articulating the failure our our academic and employment infrastucture to foster digital literacy in the public.
|
||||
|
||||
While I consider my work on the topic far from finished, the version published here represents the spirit of my work and captures the motivation behind much of my other work.
|
||||
|
||||
[Click to download](https://pogmom.me/assets/pdf/archive-feudalism-and-digital-literacy_penelope-gomez.pdf)
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
---toml
|
||||
title = "header"
|
||||
date_created = "2026-02-22"
|
||||
---
|
||||
|
||||
# Writings
|
||||
|
||||
## This is a space for thoughts I have and things I write, that I might eventually return to
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
---toml
|
||||
title = "Queer Religion"
|
||||
date_created = "2026-01-07"
|
||||
---
|
||||
|
||||
# Something about queerness and religion
|
||||
|
||||
This topic is just a concept right now! It's inspired by and dedicated to my friend Euthie, who introduced me to [The Reverent Marigold](https://musicbrainz.org/artist/4c6d5428-73f4-454b-bc93-6a8f016ee490) and their incredible music.
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
---toml
|
||||
title = "Ratgender"
|
||||
date_created = "2026-01-08"
|
||||
---
|
||||
|
||||
# Ratgender
|
||||
|
||||
This topic is just a concept right now! This one was inspired by my son, after he passed away in January 2024. It's dedicated to him, his brothers past and future, and his other parent. All of whom I miss dearly.
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
---toml
|
||||
title = "ESP32-S3 USB Authentication Key"
|
||||
date_created = "2026-02-14"
|
||||
index = 2
|
||||
---
|
||||
# ESP32-S3 USB Authentication Key
|
||||
|
||||
A DIY FIDO2 key using an ESP32-S3, a [top-to-top](/vocab) type-C coupler, and the [Pico-Fido firmware](https://github.com/polhenarejos/pico-fido).
|
||||
|
||||
This was kind of my introduction to 3D modeling, as I had gotten access to a (frustrating and poorly-maintained) 3D printer when I started my (at time of writing) current job several months prior.
|
||||
|
||||
I'd also been wanting a USB authentication key for use with my computers, but didn't want to shell out the cash for a Yubikey. I realized for the same price, I could make my own and have parts left over.
|
||||
|
||||
It works well, honestly I've no complaints with the firmware, and am happy with how the casing I printed turned out. I did wind up having to dremel some of the aligment walls for the button and shave it down.
|
||||
|
||||

|
||||

|
||||
|
||||
### Resources
|
||||
|
||||
- [Housing STL Files](https://pogmom.me/assets/stl/pico-fido.zip)
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
# project blog
|
||||
|
||||
## This will ideally create more problems than it could ever possibly solve
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
---toml
|
||||
title = "Debian on an iMac G3"
|
||||
date_created = "2026-01-08"
|
||||
date_updated = "2026-02-03"
|
||||
index = 1
|
||||
---
|
||||
# Debian Sid on an iMac G3
|
||||
|
||||

|
||||
|
||||
[Read More >](/plog/imac-g3-debian/)
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
## 2026-02-03
|
||||
|
||||
In November 2017 while thrifting with my ex, I found a tangerine iMac G3/333 with its original keyboard.
|
||||
|
||||
I had no idea if it worked, nor did I know all that much about it, architecturally speaking. However, it was $70, and this was during the time in my life that I was a rabid Apple fanatic- don't apologize for the judgements, I understand.
|
||||
|
||||
I bought it, and was thrilled to find that after bringing it back to my parents' house, it worked. It had no operating system installed, but it otherwise booted without any issues. One ebay purchase of some OS X Panther install disks later, and I had it up and fully running. I frankly don't remember too much of what I managed to do with it- I think I managed to get opera or some other contemporary browser installed on it? Though I'm not even sure if I had ever managed to get it online.
|
||||
|
||||
At some point, it turned into a very large paperweight that moved between desks, closets, and corners of rooms. But when I leapt out of the Apple frying pan in 2022/2023 and into the Linux fire, somewhere along the way I had the idea to put Debian on it.
|
||||
|
||||
The natural starting place for this was obviously Debian Jessie, the last version of the distro that officially supported the 32-bit PowerPC architecture. And wouldn't you know it? It just worked. Well, after wasting a few CDs because I'd apparently written then too quickly, if I recall correctly. I also at this point upgraded the base memory (from 256MiB, I think?) to 512MiB using an "[A0383205 512MB PC133 Memory Dell Inspiron 3700 4100](https://www.ebay.com/itm/192791214383)" Memory Stick.
|
||||
|
||||
This made for an awesome novelty project- I even got some cool terminal programs like cmatrix and hyfetch running on it, which were fun to show off to my friends. But then I found in [a MacRumors forum thread](https://web.archive.org/web/20250908055651/https://forums.macrumors.com/threads/debian-sid-installation-guide-powerpc.2146795/) that [debian-ports](https://deb.debian.org/debian-ports/) had some level of support for PowerPC, and proceeded to completely fuck up my debian install, because I couldn't be bothered to buy more blank CDs to put experimental disc images on, and tried to directly jump 4 releases at once. I hadn't gotten the opportunity to return to this project before I eventually moved to another state, into an apartment that didn't really provide me room for projects like that, so I wound up leaving it in my home town. Upon moving out of that apartment and into my (at time of writing) current residence, I reacquired it and am hoping to see just how far I can push this thing. Below is an image of the first time I booted it back up again after my move, apparently displaying the state I left this thing in.
|
||||
|
||||

|
||||
|
||||
I have no idea how viable this really is right now ([apparently the kernel might be troublesome?](https://web.archive.org/web/20260204063354/https://lists.debian.org/debian-powerpc/2020/04/msg00087.html)) but I have much more experience at this point with manual Debian installs, including on unconventional hardware. That said, I expect PowerPC support in Debian's unstable version has not gotten any better than when I first attempted this, especially seeing as Trixie (current Stable release) marked the end of support for another 32-bit architecture, i386.
|
||||
I think my next approach will focus on [booting via USB](https://idevicecollector.home.blog/2019/04/02/how-to-boot-your-powerpc-g3-g4-or-g5-from-usb-using-open-firmware-mode/), which I couldn't seem to get working in the past. But if that fails, I've got enough blank CDs now that I should be set- and I plan to document commands & post disk images that I confirm work.
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
---toml
|
||||
title = "OnePlus 6T Cyberdeck"
|
||||
date_created = "2026-02-14"
|
||||
index = 3
|
||||
---
|
||||
# OnePlus 6T "CyberDeck"
|
||||
|
||||
Linux phone
|
||||
|
||||
Linux phone
|
||||
|
||||
It feels like cheating to call this a cyberdeck since this is just kinda a linux phone glued to a bluetooth keyboard (after I 3D print some glue).
|
||||
|
||||

|
||||
|
|
@ -1,12 +0,0 @@
|
|||
---toml
|
||||
title = "Personal Website"
|
||||
date_created = "2026-03-09"
|
||||
index = 0
|
||||
---
|
||||
|
||||
# Personal Website
|
||||
|
||||
I'm rewriting my website!
|
||||
|
||||
I was inspired to do so by my friend Val who wrote her own web server in Rust using [warp](https://lib.rs/crates/warp) for her personal website (linked in the sidebar under "TerminalLesbian").
|
||||
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
---toml
|
||||
title = "To-Do List"
|
||||
date_created = "2026-02-14"
|
||||
index = 99
|
||||
---
|
||||
# to-do list
|
||||
|
||||
- Wearable Variable Power Supply
|
||||
- Arc Lighter
|
||||
- Long-range WiFi Antenna with POE
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
---toml
|
||||
title = "Pogmom Suite"
|
||||
date_created = "2026-02-14"
|
||||
index = 1
|
||||
---
|
||||
|
||||
# Pogmom Suite
|
||||
|
||||
In response to predatory data collection, hostile platform siezure, and [enshittification](/vocab#enshittification) of the centralized internet, I host a collection of online services called the 'Pogmom Suite'.
|
||||
|
||||
These services utilize federated social networking standards and rely on free and open source software as exclusively as possible.
|
||||
|
||||
While security and practicality require that registration not be open to the general public, I am more than happy to offer advice and guidance to those interested in pursuing similar projects.
|
||||
|
||||
[Learn More](https://suite.pogmom.me)
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
---toml
|
||||
title = "Project Blog"
|
||||
date_created = "2026-02-15"
|
||||
index = 5
|
||||
---
|
||||
|
||||
# Project Blog
|
||||
|
||||
I'd like to start documenting the various projects I'm working on, especially those involving niche/old hardware!
|
||||
|
||||
If you have any interest in forcing linux onto ancient computers or abysmal soldering jobs, this'll probably be your thing.
|
||||
|
||||
[Click to Visit](/plog)
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
---toml
|
||||
title = "Software"
|
||||
date_created = "2026-02-15"
|
||||
index = 2
|
||||
---
|
||||
|
||||
# Software
|
||||
|
||||
My current software development projects are primarily in Rust and Bash, targeting my own Linux server, desktop, and mobile usage.
|
||||
In the past, I have developed software for Pebble smartwatches and Apple devices, both jailbroken and stock.
|
||||
|
||||
Most of my software can be found at my [Forgejo](https://git.pogmom.me/pogmommy/), or at its mirror on [Codeberg](https://codeberg.org/pogmommy). My presence on [Github](https://github.com/pogmommy) is limited to pull requests, discussion, and other contributions toward software hosted on the platform.
|
||||
|
||||
I also package software for the Debian operating system, which I distribute on [my apt repository](https://apt.pogmom.me/).
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
## top and bottom cables
|
||||
|
||||
Saying "male" and "female" connectors doesn't make sense when i have a female penis
|
||||
|
||||
Besides, plugging in a coupler/adapter is more analagous to a strap-on than it is to surgery
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
---toml
|
||||
title = "Writing"
|
||||
date_created = "2026-02-15"
|
||||
date_updated = "2026-03-09"
|
||||
index = 4
|
||||
---
|
||||
|
||||
# Writings
|
||||
|
||||
I've been writing more! I'd like to start collecting those thoughts and sharing them somewhere again.
|
||||
|
||||
These might be less technical and more personal, veering into topics such as gender and spirituality.
|
||||
|
||||
[Click to Visit](/musings)
|
||||
48
src/lib/config.rs
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
use dirs;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
pub struct ServerConfig {
|
||||
pub server_root: PathBuf,
|
||||
pub listen_port: u16,
|
||||
pub bind_address: [u8; 4],
|
||||
pub server_domain: String,
|
||||
}
|
||||
|
||||
impl ServerConfig {
|
||||
fn read_config() -> Option<String> {
|
||||
let _test = dirs::config_dir();
|
||||
let config_paths: Vec<Option<PathBuf>> =
|
||||
vec![dirs::config_dir(), Some(PathBuf::from("/etc"))];
|
||||
let valid_conf = config_paths
|
||||
.iter()
|
||||
.find_map(|p| {
|
||||
p.as_ref().and_then(|path| {
|
||||
let config_path = path.join("mdws/config.toml");
|
||||
if config_path.exists() {
|
||||
Some(config_path)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
})
|
||||
.and_then(|p| if p.is_file() { Some(p) } else { None });
|
||||
match valid_conf {
|
||||
Some(config_file) => std::fs::read_to_string(config_file).ok(),
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_config() -> ServerConfig {
|
||||
match Self::read_config().and_then(|t| toml::from_str(t.as_str()).ok()) {
|
||||
Some(toml) => toml,
|
||||
None => ServerConfig {
|
||||
server_root: PathBuf::from("/srv/mdws/root"),
|
||||
listen_port: 3030,
|
||||
bind_address: [127, 0, 0, 1],
|
||||
server_domain: "127.0.0.1:3030".to_string(),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -102,18 +102,26 @@ pub fn curl_response(
|
|||
page_contents: Vec<MarkdownModule>,
|
||||
sidebar_contents: Vec<MarkdownModule>,
|
||||
width: Option<i32>,
|
||||
server_domain: String,
|
||||
path: &str,
|
||||
) -> Box<dyn Reply> {
|
||||
let w = width.unwrap_or(100);
|
||||
let shell_header = if width.is_none() {
|
||||
"curl -s beta.pogmom.me/?width=$(tput cols);exit 0\n".to_string()
|
||||
//"clear;curl -s beta.pogmom.me/?width=$(tput cols);exit 0\n".to_string()
|
||||
format!(
|
||||
"curl -s {}{}?width=$(tput cols);exit 0\n",
|
||||
server_domain, path
|
||||
)
|
||||
} else {
|
||||
"".to_string()
|
||||
};
|
||||
let shell_footer = if width.is_none() {
|
||||
box_content(
|
||||
&"Did you know‽\nIf you (dangerously) pipe this output to your shell, it autosizes! more interactivity is planned in the future\nex: `curl -s beta.pogmom.me | bash`".to_string(),
|
||||
w - 4)
|
||||
&format!(
|
||||
"Did you know‽\nIf you (dangerously) pipe this output to your shell, it autosizes! more interactivity is planned in the future\nex: `curl -s {}{} | bash`",
|
||||
server_domain, path
|
||||
),
|
||||
w - 4,
|
||||
)
|
||||
} else {
|
||||
"".to_string()
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,6 +1,13 @@
|
|||
use crate::markdowner::MarkdownModule;
|
||||
use std::path::PathBuf;
|
||||
use warp::Reply;
|
||||
|
||||
fn make_ascii_titlecase(s: &mut str) {
|
||||
if let Some(r) = s.get_mut(0..1) {
|
||||
r.make_ascii_uppercase();
|
||||
}
|
||||
}
|
||||
|
||||
fn get_header_html(page_contents: &Vec<MarkdownModule>) -> String {
|
||||
let header = page_contents
|
||||
.iter()
|
||||
|
|
@ -12,6 +19,13 @@ fn get_header_html(page_contents: &Vec<MarkdownModule>) -> String {
|
|||
}
|
||||
}
|
||||
|
||||
fn get_page_title(page_contents: &Vec<MarkdownModule>) -> Option<String> {
|
||||
page_contents
|
||||
.iter()
|
||||
.find(|m| m.path.file_name().unwrap_or_default().eq("header.md"))
|
||||
.map(|h| h.metadata.title.clone())
|
||||
}
|
||||
|
||||
fn get_content_html(page_contents: &Vec<MarkdownModule>) -> String {
|
||||
let content: Vec<&MarkdownModule> = page_contents
|
||||
.iter()
|
||||
|
|
@ -22,7 +36,7 @@ fn get_content_html(page_contents: &Vec<MarkdownModule>) -> String {
|
|||
.filter_map(|m| {
|
||||
markdown::to_html_with_options(m.content.as_str(), &markdown::Options::gfm())
|
||||
.ok()
|
||||
.and_then(|c| {
|
||||
.map(|c| {
|
||||
let mut paragraph_module = format!(
|
||||
"<div id='{}' class='markdown-module'>{}",
|
||||
m.path
|
||||
|
|
@ -47,7 +61,7 @@ fn get_content_html(page_contents: &Vec<MarkdownModule>) -> String {
|
|||
};
|
||||
paragraph_module = format!("{}</div>", paragraph_module);
|
||||
|
||||
Some(paragraph_module)
|
||||
paragraph_module
|
||||
})
|
||||
})
|
||||
.collect();
|
||||
|
|
@ -59,8 +73,9 @@ fn fill_template(
|
|||
content_html: String,
|
||||
sidebar_html: String,
|
||||
page_title: String,
|
||||
server_root: PathBuf,
|
||||
) -> String {
|
||||
let template_html = std::fs::read_to_string("./serve/assets/templates/page.html")
|
||||
let template_html = std::fs::read_to_string(server_root.join("assets/templates/page.html"))
|
||||
.expect("could not read page.html template");
|
||||
let template = text_template::Template::from(template_html.as_str());
|
||||
let mut values = std::collections::HashMap::new();
|
||||
|
|
@ -74,9 +89,15 @@ fn fill_template(
|
|||
pub fn html_response(
|
||||
page_contents: Vec<MarkdownModule>,
|
||||
sidebar_contents: Vec<MarkdownModule>,
|
||||
page_title: String,
|
||||
route_title: String,
|
||||
server_root: PathBuf,
|
||||
) -> Box<dyn Reply> {
|
||||
let header_html = get_header_html(&page_contents);
|
||||
let mut page_title = match get_page_title(&page_contents) {
|
||||
Some(parsed) => parsed,
|
||||
None => route_title,
|
||||
};
|
||||
make_ascii_titlecase(&mut page_title);
|
||||
|
||||
let content_html = get_content_html(&page_contents);
|
||||
let sidebar_html = get_content_html(&sidebar_contents);
|
||||
|
|
@ -86,5 +107,6 @@ pub fn html_response(
|
|||
content_html,
|
||||
sidebar_html,
|
||||
page_title,
|
||||
server_root,
|
||||
)))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,47 +42,23 @@ fn build_markdown_module(markdown_path: &PathBuf) -> Option<MarkdownModule> {
|
|||
}
|
||||
|
||||
pub fn get_markdown_modules(target_path: &PathBuf) -> Vec<MarkdownModule> {
|
||||
//if target_path.exists() {}
|
||||
|
||||
let mut mds: Vec<MarkdownModule> = std::fs::read_dir(target_path.clone())
|
||||
.expect("could not read target directory")
|
||||
.filter_map(|d| {
|
||||
println!("found file! {:?}", d);
|
||||
d.ok().and_then(|f| build_markdown_module(&f.path()))
|
||||
d.ok().and_then(|f| {
|
||||
if !f
|
||||
.file_name()
|
||||
.into_string()
|
||||
.unwrap_or_default()
|
||||
.starts_with(".")
|
||||
{
|
||||
build_markdown_module(&f.path())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
})
|
||||
.collect();
|
||||
mds.sort_by_key(|e| e.metadata.index);
|
||||
mds
|
||||
//println!("{:#?}", mds);
|
||||
/*for md in mds {
|
||||
println!("title: {:?}", md.metadata.title);
|
||||
println!("path: {:?}", md.path);
|
||||
println!("content: {:?}", md.content);
|
||||
}*/
|
||||
|
||||
/*std::fs::read_dir(target_path.clone())
|
||||
.unwrap()
|
||||
.filter(|x| {
|
||||
x.as_ref()
|
||||
.unwrap()
|
||||
.path()
|
||||
.extension()
|
||||
.unwrap_or_default()
|
||||
.eq("md")
|
||||
&& !x.as_ref().unwrap().file_name().eq("header.md")
|
||||
})
|
||||
.map(|x| x.unwrap().path())
|
||||
.collect();*/
|
||||
|
||||
/*let test = MarkdownModule {
|
||||
path: "".to_owned().into(),
|
||||
content: "".to_string(),
|
||||
metadata: Some(FrontMatter {
|
||||
title: "".to_string(),
|
||||
date_created: Some("".to_string()),
|
||||
date_updated: Some("".to_string()),
|
||||
index: Some(0),
|
||||
}),
|
||||
};
|
||||
vec![test]*/
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use crate::{MarkdownModule, markdowner};
|
||||
use crate::{MarkdownModule, config, markdowner};
|
||||
use ptree::{Style, TreeItem};
|
||||
use rand::seq::IteratorRandom;
|
||||
use std::borrow::Cow;
|
||||
|
|
@ -12,15 +12,17 @@ impl TreeItem for PathItem {
|
|||
type Child = Self;
|
||||
|
||||
fn write_self<W: io::Write>(&self, f: &mut W, style: &Style) -> io::Result<()> {
|
||||
let cwd = std::env::current_dir()
|
||||
.expect("unable to parse working directory")
|
||||
.join("serve");
|
||||
let cwd = config::ServerConfig::get_config().server_root;
|
||||
|
||||
if let Some(n) = self.0.file_name() {
|
||||
write!(
|
||||
f,
|
||||
"[{}](/{})\n",
|
||||
style.paint(n.to_string_lossy()),
|
||||
style.paint(
|
||||
n.to_str()
|
||||
.expect("could not convert file name to str")
|
||||
.replace("-", " ")
|
||||
),
|
||||
self.0
|
||||
.clone()
|
||||
.to_path_buf()
|
||||
|
|
@ -34,7 +36,7 @@ impl TreeItem for PathItem {
|
|||
}
|
||||
}
|
||||
|
||||
fn children(&self) -> Cow<[Self::Child]> {
|
||||
fn children(&self) -> Cow<'_, [Self::Child]> {
|
||||
let v = if let Ok(list) = fs::read_dir(&self.0) {
|
||||
list.filter_map(|item| item.ok())
|
||||
.map(|entry| entry.path())
|
||||
|
|
@ -43,6 +45,10 @@ impl TreeItem for PathItem {
|
|||
e.read_dir()
|
||||
.unwrap()
|
||||
.any(|x| x.unwrap().path().extension().unwrap_or_default().eq("md"))
|
||||
&& !e
|
||||
.read_dir()
|
||||
.unwrap()
|
||||
.any(|x| x.unwrap().path().file_name().unwrap().eq(".secret"))
|
||||
} else {
|
||||
false
|
||||
}
|
||||
|
|
@ -57,28 +63,32 @@ impl TreeItem for PathItem {
|
|||
}
|
||||
}
|
||||
|
||||
fn random_image_module(module: &MarkdownModule, directory: &str) -> MarkdownModule {
|
||||
let image = random_image(directory);
|
||||
fn random_image_module(
|
||||
module: &MarkdownModule,
|
||||
directory: &str,
|
||||
server_root: PathBuf,
|
||||
) -> MarkdownModule {
|
||||
let image = random_image(directory, server_root.clone());
|
||||
let template_content = module.content.clone();
|
||||
let template = text_template::Template::from(template_content.as_str());
|
||||
let mut values = std::collections::HashMap::new();
|
||||
|
||||
values.insert(
|
||||
"file_path",
|
||||
let image_path = format!(
|
||||
"/{}",
|
||||
image
|
||||
.strip_prefix("./serve/")
|
||||
.strip_prefix(server_root)
|
||||
.unwrap()
|
||||
.to_str()
|
||||
.unwrap_or_default(),
|
||||
);
|
||||
values.insert(
|
||||
"file_name",
|
||||
image
|
||||
.file_stem()
|
||||
.unwrap_or_default()
|
||||
.to_str()
|
||||
.unwrap_or_default(),
|
||||
);
|
||||
values.insert("file_path", image_path.as_str());
|
||||
let image_name = image
|
||||
.file_stem()
|
||||
.unwrap_or_default()
|
||||
.to_str()
|
||||
.unwrap_or_default()
|
||||
.replace("_", " ");
|
||||
values.insert("file_name", image_name.as_str());
|
||||
let module = MarkdownModule {
|
||||
path: module.path.clone(),
|
||||
content: template.fill_in(&values).to_string(),
|
||||
|
|
@ -87,14 +97,19 @@ fn random_image_module(module: &MarkdownModule, directory: &str) -> MarkdownModu
|
|||
module.clone()
|
||||
}
|
||||
|
||||
pub fn sidebar_content(target_path: &PathBuf, root_path: PathBuf) -> Vec<MarkdownModule> {
|
||||
pub fn sidebar_content(
|
||||
target_path: &PathBuf,
|
||||
root_path: PathBuf,
|
||||
counter: i32,
|
||||
path: &str,
|
||||
) -> Vec<MarkdownModule> {
|
||||
let sidebar_modules = markdowner::get_markdown_modules(target_path);
|
||||
let sidebar_modules: Vec<MarkdownModule> = sidebar_modules
|
||||
.iter()
|
||||
.map(|f| match f.metadata.title.as_str() {
|
||||
"rats" => random_image_module(f, "rats"),
|
||||
"buttons" => random_image_module(f, "buttons"),
|
||||
"sitemap" => sitemap(f, &root_path),
|
||||
"rats" => random_image_module(f, "rats", root_path.clone()),
|
||||
"buttons" => random_image_module(f, "buttons", root_path.clone()),
|
||||
"sitemap" => sitemap(f, &root_path, counter, path),
|
||||
_ => MarkdownModule {
|
||||
path: f.path.clone(),
|
||||
content: f.content.clone(),
|
||||
|
|
@ -105,11 +120,32 @@ pub fn sidebar_content(target_path: &PathBuf, root_path: PathBuf) -> Vec<Markdow
|
|||
sidebar_modules
|
||||
}
|
||||
|
||||
fn sitemap(module: &MarkdownModule, root_path: &PathBuf) -> MarkdownModule {
|
||||
fn sitemap(
|
||||
module: &MarkdownModule,
|
||||
root_path: &PathBuf,
|
||||
counter: i32,
|
||||
path: &str,
|
||||
) -> MarkdownModule {
|
||||
let mut writer: Vec<u8> = Vec::new();
|
||||
let dir = PathItem(root_path.clone());
|
||||
|
||||
ptree::write_tree(&dir, &mut writer).expect("could not write tree");
|
||||
let ptree_chars = ptree::print_config::IndentChars {
|
||||
down_and_right: "┣".to_string(),
|
||||
down: "┃".to_string(),
|
||||
turn_right: "┗".to_string(),
|
||||
right: "━".to_string(),
|
||||
empty: " ".to_string(),
|
||||
};
|
||||
let ptree_conf = ptree::PrintConfig {
|
||||
depth: 20,
|
||||
indent: 3,
|
||||
padding: 1,
|
||||
styled: ptree::print_config::StyleWhen::Never,
|
||||
characters: ptree_chars,
|
||||
branch: ptree::Style::default(),
|
||||
leaf: ptree::Style::default(),
|
||||
};
|
||||
ptree::write_tree_with(&dir, &mut writer, &ptree_conf).expect("could not write tree");
|
||||
|
||||
let template_content = module.content.clone();
|
||||
let template = text_template::Template::from(template_content.as_str());
|
||||
|
|
@ -117,7 +153,11 @@ fn sitemap(module: &MarkdownModule, root_path: &PathBuf) -> MarkdownModule {
|
|||
|
||||
let tree_text = String::from_utf8(writer).expect("could not parse string from utf8");
|
||||
|
||||
let visit_count = format!("{}", counter);
|
||||
|
||||
values.insert("sitemap", tree_text.as_str());
|
||||
values.insert("location", path);
|
||||
values.insert("visit_count", &visit_count.as_str());
|
||||
|
||||
let modu = MarkdownModule {
|
||||
path: module.path.clone(),
|
||||
|
|
@ -127,8 +167,8 @@ fn sitemap(module: &MarkdownModule, root_path: &PathBuf) -> MarkdownModule {
|
|||
modu.clone()
|
||||
}
|
||||
|
||||
fn random_image(directory: &str) -> PathBuf {
|
||||
let rat_image = std::fs::read_dir(PathBuf::from("./serve/assets/img/random/").join(directory))
|
||||
fn random_image(directory: &str, server_root: PathBuf) -> PathBuf {
|
||||
let rat_image = std::fs::read_dir(server_root.join("assets/img/random/").join(directory))
|
||||
.expect("where the fuck are your rat pictures?")
|
||||
.map(|f| f.expect("umm what is this?").path())
|
||||
.choose(&mut rand::rng())
|
||||
|
|
|
|||
152
src/main.rs
|
|
@ -1,10 +1,13 @@
|
|||
#![warn(unused_extern_crates)]
|
||||
#![allow(clippy::style)]
|
||||
|
||||
use http::{Uri, uri::Authority};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::path::PathBuf;
|
||||
|
||||
use warp::{Filter, filters::path::FullPath};
|
||||
#[path = "lib/config.rs"]
|
||||
mod config;
|
||||
#[path = "lib/curl.rs"]
|
||||
mod curl;
|
||||
#[path = "lib/html.rs"]
|
||||
|
|
@ -14,7 +17,8 @@ mod markdowner;
|
|||
#[path = "lib/sidebar.rs"]
|
||||
mod sidebar;
|
||||
use crate::{
|
||||
curl::curl_response, html::html_response, markdowner::MarkdownModule, sidebar::sidebar_content,
|
||||
config::ServerConfig, curl::curl_response, html::html_response, markdowner::MarkdownModule,
|
||||
sidebar::sidebar_content,
|
||||
};
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
|
|
@ -22,40 +26,99 @@ struct WebQuery {
|
|||
width: Option<i32>,
|
||||
}
|
||||
|
||||
fn router(request_path: PathBuf) -> PathBuf {
|
||||
std::env::current_dir()
|
||||
.expect("unable to determine current directory")
|
||||
.join("serve")
|
||||
.join(request_path)
|
||||
}
|
||||
|
||||
fn renderer(path: FullPath, user_agent: String, query: WebQuery) -> Box<dyn warp::Reply> {
|
||||
fn renderer(
|
||||
path: FullPath,
|
||||
user_agent: String,
|
||||
query: WebQuery,
|
||||
host: Option<Authority>,
|
||||
config: ServerConfig, //server_root: PathBuf,
|
||||
) -> Box<dyn warp::Reply> {
|
||||
println!("{:?} requested by {}", path, user_agent);
|
||||
let request_path: PathBuf = path.as_str().strip_prefix("/").unwrap_or_default().into();
|
||||
let target_path = router(request_path);
|
||||
if !target_path.exists() || target_path.is_file() {
|
||||
return Box::new(warp::redirect(
|
||||
warp::http::uri::Builder::new()
|
||||
.authority(".")
|
||||
.build()
|
||||
.unwrap(),
|
||||
));
|
||||
let target_path = config.server_root.join(request_path.clone());
|
||||
if !target_path.exists()
|
||||
|| target_path.is_file()
|
||||
|| (request_path.starts_with("assets") && target_path.is_dir())
|
||||
{
|
||||
return Box::new(warp::redirect(Uri::from_static("/")));
|
||||
}
|
||||
|
||||
// this list will grow
|
||||
let ai_user_agent_list = vec![
|
||||
"GPTBot",
|
||||
"openai",
|
||||
"ChatGPT",
|
||||
"Claude",
|
||||
"CCBot",
|
||||
"anthropic",
|
||||
"PerplexityBot",
|
||||
"Amazonbot",
|
||||
"Googlebot",
|
||||
];
|
||||
|
||||
if ai_user_agent_list.iter().any(|ua| user_agent.contains(ua)) {
|
||||
return Box::new(warp::reply::with_status(
|
||||
"llms breaks the internet and our world, go fuck yourself",
|
||||
warp::http::StatusCode::OK,
|
||||
));
|
||||
};
|
||||
|
||||
let target_page_visits = target_path.join(".visits");
|
||||
let mut counter = if target_page_visits.exists() {
|
||||
std::fs::read_to_string(target_page_visits.clone())
|
||||
.unwrap_or_default()
|
||||
.parse::<i32>()
|
||||
.unwrap()
|
||||
} else {
|
||||
0
|
||||
};
|
||||
|
||||
let ua_filter = [
|
||||
"Safari", "curl", "Windows", "Mac OS", "Linux", "iPhone", "iPad", "Android",
|
||||
];
|
||||
|
||||
if host.is_some_and(|x| x.as_str().eq("pogmom.me"))
|
||||
&& ua_filter.iter().any(|ua| user_agent.contains(ua))
|
||||
{
|
||||
if target_page_visits.exists() {
|
||||
if target_page_visits
|
||||
.metadata()
|
||||
.unwrap()
|
||||
.modified()
|
||||
.unwrap()
|
||||
.elapsed()
|
||||
.unwrap()
|
||||
.as_secs()
|
||||
.gt(&1)
|
||||
{
|
||||
counter = counter + 1;
|
||||
let _ = std::fs::write(target_page_visits, format!("{}", counter));
|
||||
}
|
||||
} else {
|
||||
counter = counter + 1;
|
||||
let _ = std::fs::write(target_page_visits, format!("{}", counter));
|
||||
}
|
||||
};
|
||||
println!("serving path: {:?}", target_path);
|
||||
|
||||
let page_contents = markdowner::get_markdown_modules(&target_path);
|
||||
let sidebar_dir = PathBuf::from("assets/sidebar/");
|
||||
|
||||
let sidebar_contents = sidebar_content(
|
||||
&router(sidebar_dir),
|
||||
std::env::current_dir()
|
||||
.expect("unable to determine current directory")
|
||||
.join("serve"),
|
||||
&config.server_root.join(sidebar_dir),
|
||||
config.server_root.clone(),
|
||||
counter,
|
||||
path.as_str(),
|
||||
);
|
||||
|
||||
let response = if user_agent.starts_with("curl/") {
|
||||
curl_response(page_contents, sidebar_contents, query.width)
|
||||
curl_response(
|
||||
page_contents,
|
||||
sidebar_contents,
|
||||
query.width,
|
||||
config.server_domain,
|
||||
path.as_str(),
|
||||
)
|
||||
} else {
|
||||
html_response(
|
||||
page_contents,
|
||||
|
|
@ -66,6 +129,7 @@ fn renderer(path: FullPath, user_agent: String, query: WebQuery) -> Box<dyn warp
|
|||
.to_str()
|
||||
.unwrap_or_default()
|
||||
.to_owned(),
|
||||
config.server_root,
|
||||
)
|
||||
};
|
||||
response
|
||||
|
|
@ -73,17 +137,47 @@ fn renderer(path: FullPath, user_agent: String, query: WebQuery) -> Box<dyn warp
|
|||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
println!("Hello, world!");
|
||||
let assets = warp::path("assets").and(warp::fs::dir("./serve/assets/"));
|
||||
let favicon = warp::path("favicon.ico").and(warp::fs::file("./serve/favicon.ico"));
|
||||
let config = ServerConfig::get_config();
|
||||
println!(
|
||||
"Serving content at {:?} on {}.{}.{}.{}:{}",
|
||||
config.server_root,
|
||||
config.bind_address[0],
|
||||
config.bind_address[1],
|
||||
config.bind_address[2],
|
||||
config.bind_address[3],
|
||||
config.listen_port
|
||||
);
|
||||
let assets = warp::path("assets").and(warp::fs::dir(config.server_root.join("assets")));
|
||||
let favicon = warp::path("favicon.ico").and(warp::fs::file(
|
||||
config.server_root.join("assets/img/favicon.ico"),
|
||||
));
|
||||
let robots =
|
||||
warp::path("robots.txt").and(warp::fs::file(config.server_root.join("assets/robots.txt")));
|
||||
//let robots = include_str!("../robots.txt").to_string().clone();
|
||||
|
||||
let markdowns = warp::any() //path::end()
|
||||
// let robots_reply = warp::path("robots.txt").map(||warp::reply::with_status(robots, warp:http::StatusCode::OK));
|
||||
|
||||
/* .and(warp::reply::with_status(
|
||||
format!("{}", robots.clone()),
|
||||
warp::http::StatusCode::OK,
|
||||
)); //.and(warp::reply::with_status(robots, warp::http::StatusCode::OK)); */
|
||||
|
||||
let config_clone = config.clone();
|
||||
|
||||
let markdowns = warp::any()
|
||||
.and(warp::path::full())
|
||||
.and(warp::header("user-agent"))
|
||||
.and(warp::query::<WebQuery>())
|
||||
.map(|path: FullPath, agent: String, query: WebQuery| renderer(path, agent, query));
|
||||
.and(warp::host::optional())
|
||||
.map(
|
||||
move |path: FullPath, agent: String, query: WebQuery, host| {
|
||||
renderer(path, agent, query, host, config_clone.clone())
|
||||
},
|
||||
);
|
||||
|
||||
let routes = favicon.or(assets).or(markdowns);
|
||||
let routes = robots.or(favicon).or(assets).or(markdowns);
|
||||
|
||||
warp::serve(routes).run(([0, 0, 0, 0], 3030)).await;
|
||||
warp::serve(routes)
|
||||
.run((config.bind_address, config.listen_port))
|
||||
.await;
|
||||
}
|
||||
|
|
|
|||