I'd like to start this post by doing a little preface, a rant, about the last weeks of my life. I've experienced various combinations of things that made me question my existence in many ways, as you can clearly see by how these entries are written I'm pretty much a young and immature idiot, and in my age such questioning is normal. If it was just for that, then I'd be OK, but this existential crisis came in a moment where I'm already shaken by a lot of serious personal issues, whose levels of intimacy are so high I'd rather not share here, and thus I felt like the whole world came crashing down on me.
What bothers me the most now is how grim the world seems, you don't realize how cruel everybody is until you look further into it. Society is so eerily judgmental of everything, and everyone, in a way that seems like all hate eachother. One might see this and think the only sane and morally coherent thought is: this world deserves to be destroyed, but this isn't an act of rage against the world, it's a disguised manifestation of it, it's proof that it won, and seeing this raw cruelty in yourself is scary. By the way, I'm not advocating for "good thing-ism", I think it's inherently naive, crossing the borders of dumb, I'm advocating for we to start thinking before being so needlessly violent to others.
Yet another thing that scares me is how huge this world is, it's also something that fascinates me. There are so many possibilities, but it feels like most of them have already been explored, repeated, that's something I hate. It feels like every once in a while I discover that the quirky, the rare, the bizarre are actually quite unremarkable, nothing is special, I also forget this every once in a while for obvious reasons. I'm afraid of running out of ideas and crying in the corner like the uncreative failure I am.
Then there is myself, I have completely lost any semblance of coherence I managed to gather over the past months and started dissociating again. I don't know who I am anymore, really don't, and it isn't helping.
Well, hello! I've written that previous post in a rush, a big rush, so I glossed over quite a few details, this one is close to what I intended for it to be.
First, I want to clear up is my motivation to migrate from Blogger to my own custom CMS system, the answer is simple, I want independence from Google due to this (specially since 26 year old, extremely used Google Earth Pro is being annihilated), plus sometimes I feel a need to add some weird features that this place just can't handle. I could really use any CMS, WordPress, Drupal (lovely) or anything smaller, but they all run on PHP and SQL, and my only options for servers are a Pi Zero or a cheap VPS, so I decided to make my own extremely-ultra-super-compact software using Rust, which I've been trying to use to make a complete project since at least the year before the last.
Well, you see, I also forgot to tell that the whole thing is in Codeberg, I'm actually making it so I develop my code in my local machine, upload to Codeberg, then just run the deliciously simple cargo install --git and update the software in an instant in my server. The repo is at sandmiz/nightcirrus-fibratus (not to be confused with the Windows security analysis software), the name "fibratus" comes from a type of cirrus, pretty but one of the simpler ones, composed basically of ice crystal "fibers" and nothing more.
The first thing you should do when making a CMS is, pretty obviously, make a web server, so I just copied an example server from the Rust Book. I decided that I'd handle multithreading with the smol async library, rather than more popular alternatives. As the name implies, it's very small and therefore perfect for this project. I wanted the most basic and important files to be loaded and served from RAM, things like interface images, HTML, CSS and JS files for very static pages, so I coded a build.rs file that took all files from a resources folder and baked them directly into constant maps using phf. Then I made a small parser for reading the URI from the HTTP request and serving the files from the maps.
I separated the entry point from the serving module and expanded on it, adding things such as API endpoints and serving from the filesystem. Currently, the net.rs file contains basically two parts: the header reader and the router. The header reader does exactly that, reads headers, more specifically it checks the first line for method, URI and query parameters, then extracts an authentication cookie and the Content-Length header. The router is just a huge match that checks the URI and method, which includes performing regex shenanigans, and routes it to either the RAM static server, an API function or the filesystem static server. Any errors in the first part close the stream, while the ones in the second part are returned as 500.
One particular thing that bothered me while making this router was regex, I didn't want to include it at first but for URL argument processing it's really mandatory. To lower any performance penalty that might happen, I used the regex_macro crate to compute a lazy expression that would only compile once. I also considered making the router a macro that'd expand to a match, to make the regex paths less grotesque and different from the static paths. Such thing proved EXTREMELY hard with declarative macros and I didn't want to bother learning procedural macros, so I had to compromise with only shortening the capture groups obtaining part of the match arms.
So, with this ready, we can go on to the interesting parts, persist my content data I at first wanted to use SQLite, but thinking about it better made me realize that by doing that I was introducing a heavy parser and yet another failure point, so I opted to making the database system myself. I wanted to make a zero-copy binary database that'd map directly to memory addresses to minimize IO headaches, but while searching around I found a library which already did all that: heed, an LMDB wrapper that is really a beautiful and divine thing, seriously. LMDB is a key-value system that accepts basically anything as key and value, and heed wraps around it in a very pretty manner, including some functions I'd need for later for finding for comments that belong to a post.
Borrowing from my knowledge working with actual web frameworks in more high level languages, I made 2 "entity" structs: Post, which is self explanatory it's just a dynamic post, and Comment, which is also self explanatory it's just a comment in a post, but I intend to use it to achieve forum-like behavior later. To manage these entities I used repository structs, that'd wrap a heed Database. I think this was the more hassle-free part of the whole process, still thought, I dealt with some problems due to dealing very low-level binary values.
My biggest trouble here was endianess, God how annoying this was. Basically my IDs were all plain integers, and the comment ones were all prefixed by the ID of the post they belong to. This made my life hell as I needed to convert them all to big-endian values both when inserting and reading, and sometimes stuff'd break so hard among the numerous bitwise operations it made me insane.
Endianess.
With the content stored, I wanted the post pages to be built to the filesystem and served from there rather than being rendered in the client, so I made subsystem for this too. I adapted the build script to take some special files from the resources and turn them into macros that use format! templates to automatically build a page from dynamic variables, basically just View templating stuff. This happens in a function called build_dyn, which fetches all posts from the DB to build the indexes and post pages, then saves them in the static serving folder. A peculiar thing about that function is that it uses a lot of ring buffers to somewhat efficiently fetch the most recent posts.
The Lord of the Rings.
Also, I probably should not store all template filenames that are clearly distinct from the others and should be ignored by the default builder in a handmade array in the build script, I shoooould reaaaaally check their names programatically, just a little thought.
Isn't that so inefficient?
The last part was making the API functions, it was 90% boring repetitive CRUD, specially for the comments, with some sprinkles of other stuff. I tried sending most stuff simply stacked with implied structure via newlines rather than using JSON, because let's be honest JSON parsing is not that lightweight. I think the most different thing here was the function that uploads profile pictures for users, which initially parsed the sent image as base64, had some scary threading stuff and was home to the a rage enducing Rust trolling involving the Result monad. The compiler was throwing me such a non-sense error, and I seriously searched for hours until I found a GitHub issue which was full of people as mad as me and some ugly workarounds. Try to find the problem in the image below:
Very stupid complaint.
oh Rust,
why do ye complain?
why do ye complain so much?
be explicit, isn't it what ye want?
oh Rust,
please don't complain so much
because I wasn't very clear
isn't it obvious my dear?
Talking about comments and users, the way I handled them was that both logging in and registering would take the least amount of brainpower (and storage) possible. You click a button, receive or put a token, that's you forever, and there are 2¹²⁸ token possibilities! No way someone repeats! Of course the token is hashed using SHAKE128, 2704 times, XORed all the way along, which all should prevent some sneaky little script kiddie pretending to be another person. Still, everything is fully stateless (except for a nickname database I added because recognizing someone by their hash is impossible and for the profile pictures because yeah why not but FORGET IT) so it takes almost 0 database space with passwords and usernames and e-mails and all that clunky stuff that makes everything so clunky, it's kinda like tripcodes for image boards but you don't choose a name nor password.
Comments.
Permissions are handled in the dumbest, simplest way possible, access the website through a special port? Boom, you have admin privileges and can edit and delete anything. This port is only accessible from my local internet so it doesn't really matter. I do plan on making it accessible for other people though, I just don't know how I will make them able to access this port without leaving it open to the world.
The main port is made accessible by, unfortunately, a Cloudflare Tunnel, I live behind CGNAT and this was the most viable way of making the server public. I do hope I can get hid of this CDN disgrace and don't have to rely on cloud providers in the future thought, but that's the best I can do now. On the bright side of things, I don't have to code a DDOS and anti-bot protector to shield my teeny weeny Zero from getting bombarded by the malign machines of some random botnet, although I do have good ideas on how to implement that.
For now I think the biggest feature missing is possibly little template things that you can easily add to a post and the SSG auto bakes them in your page. As for code quality, it's abysmal, I'm not a good coder, it needs a LOT more logging and potentially a lot more comments, and better error handling too. I actually need to record every interaction as to comply with federal regulations, as I'm a very law abiding person that doesn't want to be nuked by the government, and I also need to record every error as to comply with common sense, as I'm not dumb enough to leave most error silent (NEVER make your errors silently, do the opposite, make them really SCREAM with TERROR).
I should really use this little logger more, it's catching dust:
Dustball.
Anyways, that's it for the backend, I have quite a lot more stuff to talk about but I'll finish the frontend first before coming here again with an update. I hope once everything is ready I can post more and focus on my little projects.
Thank you! Keep going.
-A. 27/09/2026, 2 in the morning.
Nenhum comentário
Postar um comentário