Compare commits
39 Commits
rust-nix-r
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| fb23b004ce | |||
| ce0edf6060 | |||
| 53b19fdd9b | |||
| 463553e347 | |||
| 9b4b5193c6 | |||
| da0726f22f | |||
| 83e6dc7ccc | |||
| 96c1840d31 | |||
| 3e0850fc99 | |||
| 06c4c9f5f7 | |||
| 7f3b1f08e3 | |||
| d67456b9d7 | |||
| 47b8acf976 | |||
| 55f7315816 | |||
| d6ef911f20 | |||
| 000661ef3d | |||
| 94c07109ce | |||
| 970a249775 | |||
| ecfd1b0e8d | |||
| 35257bb6e0 | |||
| 62348a5571 | |||
| 2d14dd2b93 | |||
| 1ec966a9a1 | |||
| 99e391fe91 | |||
| 84d1e3e9c7 | |||
| 3a44e7c4cd | |||
| eb5cc3c3b0 | |||
| f9d268b3e5 | |||
| ca37894e8b | |||
| e04f0a3712 | |||
| 2e01a32802 | |||
| 51e2e1eea4 | |||
| 351dfc2734 | |||
| 36a4af39f7 | |||
| ea1ee244e2 | |||
| 18023732d4 | |||
| 7251f68b6f | |||
| 94b6609ea4 | |||
| 6b94076d04 |
@@ -1,4 +1,4 @@
|
|||||||
name: Build and Test
|
name: Build
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
@@ -8,18 +8,21 @@ on:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
runs-on: production-runner
|
runs-on: self-hosted
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Pull
|
||||||
uses: actions/checkout@v4
|
working-directory: /opt/site
|
||||||
|
run: git pull origin main
|
||||||
- name: Kill existing process
|
|
||||||
run: |
|
|
||||||
pkill site || true
|
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
|
working-directory: /opt/site
|
||||||
run: cargo build --release
|
run: cargo build --release
|
||||||
|
|
||||||
- name: Start Application
|
- name: Restart site
|
||||||
|
working-directory: /opt/site
|
||||||
run: |
|
run: |
|
||||||
|
pkill site || true
|
||||||
|
export RUNNER_TRACKING_ID=self-managed
|
||||||
nohup ./target/release/site > server.log 2>&1 &
|
nohup ./target/release/site > server.log 2>&1 &
|
||||||
|
sleep 1
|
||||||
|
pgrep site && echo "Site is running!"
|
||||||
|
|||||||
17
src/main.rs
17
src/main.rs
@@ -28,27 +28,27 @@ fn load_posts() -> Result<HashMap<String, Post>, Error> {
|
|||||||
let title = re_title.captures(&typst)
|
let title = re_title.captures(&typst)
|
||||||
.expect(format!("Post title not found in {}", dir_path.to_str().unwrap()).as_str())
|
.expect(format!("Post title not found in {}", dir_path.to_str().unwrap()).as_str())
|
||||||
.get(1).unwrap().as_str().trim().to_string();
|
.get(1).unwrap().as_str().trim().to_string();
|
||||||
println!("{}", title);
|
|
||||||
let slug = re_slug.captures(&typst)
|
let slug = re_slug.captures(&typst)
|
||||||
.expect("Post slug not found")
|
.expect("Post slug not found")
|
||||||
.get(1).unwrap().as_str().trim().to_string();
|
.get(1).unwrap().as_str().trim().to_string();
|
||||||
let summary = re_summary.captures(&typst)
|
let summary = re_summary.captures(&typst)
|
||||||
.expect("Post summary not found")
|
.expect("Post summary not found")
|
||||||
.get(1).unwrap().as_str().trim().to_string();
|
.get(1).unwrap().as_str().trim().to_string();
|
||||||
let img_path = dir?.path().join(
|
let img_path = "/".to_string() + dir?.path().join(
|
||||||
re_img.captures(&typst)
|
re_img.captures(&typst)
|
||||||
.expect("Post preview image not found")
|
.expect("Post preview image not found")
|
||||||
.get(1).unwrap().as_str().trim().to_string()
|
.get(1).unwrap().as_str().trim().to_string()
|
||||||
).to_str().expect("Failed converting path to string").to_string();
|
).to_str().expect("Failed converting path to string");
|
||||||
posts.insert(
|
posts.insert(
|
||||||
slug.clone(),
|
slug.clone(),
|
||||||
Post {
|
Post {
|
||||||
slug: slug,
|
slug: slug,
|
||||||
title: title,
|
title: title,
|
||||||
preview_image: img_path,
|
preview_image: img_path.clone(),
|
||||||
summary: summary,
|
summary: summary,
|
||||||
render: Html(PostTemplate {
|
render: Html(PostTemplate {
|
||||||
content: typst_to_html(typst),
|
content: typst_to_html(typst),
|
||||||
|
image: img_path
|
||||||
}.render().expect("Failed rendering post")),
|
}.render().expect("Failed rendering post")),
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@@ -59,6 +59,7 @@ fn load_posts() -> Result<HashMap<String, Post>, Error> {
|
|||||||
#[derive(Template)]
|
#[derive(Template)]
|
||||||
#[template(path = "post.html")]
|
#[template(path = "post.html")]
|
||||||
struct PostTemplate {
|
struct PostTemplate {
|
||||||
|
image: String,
|
||||||
content: String,
|
content: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,8 +69,8 @@ struct Post {
|
|||||||
title: String,
|
title: String,
|
||||||
preview_image: String,
|
preview_image: String,
|
||||||
summary: String,
|
summary: String,
|
||||||
slug: String,
|
render: Html<String>,
|
||||||
render: Html<String>
|
slug: String
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -138,7 +139,7 @@ async fn main() {
|
|||||||
.route("/posts/{slug}", get(|slug| get_post(slug, posts)))
|
.route("/posts/{slug}", get(|slug| get_post(slug, posts)))
|
||||||
.nest_service("/static", ServeDir::new("static"));
|
.nest_service("/static", ServeDir::new("static"));
|
||||||
|
|
||||||
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
|
let listener = tokio::net::TcpListener::bind("0.0.0.0:42069").await.unwrap();
|
||||||
println!("Running on http://127.0.0.1:3000");
|
println!("Running on http://127.0.0.1:42069");
|
||||||
axum::serve(listener, site).await.unwrap();
|
axum::serve(listener, site).await.unwrap();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
= About me
|
= About me
|
||||||
I enjoy competeing in capture the flag competitions (cybersecurity and the backyard game), working with robotics, automating tasks and thinking about problems.
|
I enjoy competeing in capture the flag competitions (cybersecurity and the backyard game), working with robotics, automating tasks and thinking through problems.
|
||||||
|
|
||||||
I've competed in a few hackathons which are cool, but I prefer spending large time frames incrementally understand solutions to much larger problems, and attempting solutions to those.
|
I've competed in a few hackathons which are cool, but I prefer spending large time frames incrementally understand solutions to much larger problems, and attempting solutions to those.
|
||||||
|
|
||||||
@@ -7,7 +7,7 @@ I think automation is one of the most powerful abilities we have. As a species w
|
|||||||
|
|
||||||
An odd and maybe abstract thing I enjoy is design philosphy. Some examples are creating software to building moral structures through logic to methods of learning new skills.
|
An odd and maybe abstract thing I enjoy is design philosphy. Some examples are creating software to building moral structures through logic to methods of learning new skills.
|
||||||
|
|
||||||
NixOS has to be my favourite distribution of Linux. My attraction to its design philosphy is that if something works, it will always work anywhere, and can still be worked on long after everything around it has moved on. Its hard to put into words but this feels like debian level stability with arch-like freedom to explore software.
|
NixOS has to be my favourite distribution of Linux. My attraction to its design philosophy is that if something works, it will always work anywhere, and can still be worked on long after everything around it has moved on. Its hard to put into words but this feels like debian level stability with arch-like freedom to explore software.
|
||||||
|
|
||||||
When I'm not nerding out on math, software, ethics, and optimal ways of learning things, I manage to get outdoors. I like mountain biking through trails, snowboarding, hiking through scenic areas, swimming, and basketball. The next thing I'll be getting into is likely SCUBA diving.
|
When I'm not nerding out on math, software, ethics, and optimal ways of learning things, I manage to get outdoors. I like mountain biking through trails, snowboarding, hiking through scenic areas, swimming, and basketball. The next thing I'll be getting into is likely SCUBA diving.
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#let post_slug = "building-a-drone"
|
#let post_slug = "building-a-drone"
|
||||||
#let post_preview_image = "top.jpg"
|
#let post_preview_image = "top.jpg"
|
||||||
#let post_summary = "Using a raspberry pi and 3d printer to build a drone from scratch"
|
#let post_summary = "Arduino & Pi powered 3D printed drone"
|
||||||
|
|
||||||
= Building a Drone
|
= Building a Drone
|
||||||
|
|
||||||
@@ -21,7 +21,6 @@ wires. I went with the Elegoo Neptune 3 as the printer was open-source
|
|||||||
and had a much better cost-to-utility than proprietary printers such as
|
and had a much better cost-to-utility than proprietary printers such as
|
||||||
the Ender series. I am not sponsored I just really like the printer.
|
the Ender series. I am not sponsored I just really like the printer.
|
||||||
|
|
||||||
|
|
||||||
The flight computer was the most difficult part to program. Using an
|
The flight computer was the most difficult part to program. Using an
|
||||||
ultrasonic distance sensor, gyroscope, and accelerometer the drone has
|
ultrasonic distance sensor, gyroscope, and accelerometer the drone has
|
||||||
enough information to probably never crash. The autopilot is implemented
|
enough information to probably never crash. The autopilot is implemented
|
||||||
@@ -35,6 +34,9 @@ Pi Pico. There is also a 2.4GHz line of sight receiver for manual control.
|
|||||||
A future upgrade may contain a SIM card for near-infinite remote control
|
A future upgrade may contain a SIM card for near-infinite remote control
|
||||||
connection, but drone regulations would make this difficult.
|
connection, but drone regulations would make this difficult.
|
||||||
|
|
||||||
|
#image("/static/posts/drone/graphing.jpg")
|
||||||
|
Testing the remote controller
|
||||||
|
|
||||||
The power system is the most physically challenging portion of the
|
The power system is the most physically challenging portion of the
|
||||||
drone. The motors took 14.6 Volts, while the UNO microcontroller took 5
|
drone. The motors took 14.6 Volts, while the UNO microcontroller took 5
|
||||||
Volts, and the Pico and most sensors run at 3.3 Volts. All of the power
|
Volts, and the Pico and most sensors run at 3.3 Volts. All of the power
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#let post_slug = "gamedev"
|
#let post_slug = "gamedev"
|
||||||
#let post_preview_image = "flying.webp"
|
#let post_preview_image = "flying.webp"
|
||||||
#let post_summary = "Creating an open world multiplayer terrain destruction game in Godot and Rust"
|
#let post_summary = "Coding an open world multiplayer terrain destruction game in Godot and Rust"
|
||||||
|
|
||||||
= Game Development
|
= Game Development
|
||||||
|
|
||||||
|
|||||||
@@ -1,27 +1,27 @@
|
|||||||
#let post_slug = "selfhosting-nextcloud"
|
#let post_slug = "selfhosting-nextcloud"
|
||||||
#let post_preview_image = "logo.svg"
|
#let post_preview_image = "logo.svg"
|
||||||
#let post_summary = "Why and how I host a fileserver locally, on an old laptop"
|
#let post_summary = "Selfhosting a file server on an old laptop"
|
||||||
|
|
||||||
= Selfhosting Nextcloud
|
= Selfhosting Nextcloud
|
||||||
Access it #link("nextcloud.jeremyjanella.com")[here]. Obviously you'd need a username and password first, good luck getting that!
|
#link("https://nextcloud.jeremyjanella.com")[My Nextcloud]
|
||||||
|
|
||||||
=== Why I selfhost Nextcloud
|
== Why I selfhost Nextcloud
|
||||||
I'm a constantly-commuting university student. I primarily work on my desktop, take notes and attend lectures and events with my laptop, and review, access, and share documents from my phone. I need the same files to be accessible on all devices at all times.
|
I'm a constantly-commuting university student. I primarily work on my desktop, take notes and attend lectures and events with my laptop, and review, access, and share documents from my phone. I need the same files to be accessible on all devices at all times.
|
||||||
|
|
||||||
Nextcloud is a self-hosted, batteries included file server primarily. It also has a working office suite for editing documents on the cloud through your browser, and numerous other extensions.
|
Nextcloud is a self-hosted, batteries included file server primarily. It also has a working office suite for editing documents on the cloud through your browser, and numerous other extensions.
|
||||||
|
|
||||||
Mega, Google Drive, and OneDrive all do this. Why would I bother using nextcloud? Isn't that reinventing the wheel? Yes and no.
|
Mega, Google Drive, and OneDrive all do this. Why would I bother using nextcloud? Isn't that reinventing the wheel? Yes and no.
|
||||||
|
|
||||||
===== Do tech giants respect privacy? Nextcloud does.
|
=== Do tech giants respect privacy? Nextcloud does.
|
||||||
With remotely managed cloud providers, you're trusting large corporations not to look at your data. You are, for free, letting profit-driven entities, to store your data. Of course they could claim to be implementing end-to-end encryption, or that the free tier is a trail of their paid subscription services. As far as I know none of this is open source either, so you're just trusting big-data to hold your data for free without looking at your data. I just don't trust them not to look at my assignments I guess.
|
With remotely managed cloud providers, you're trusting large corporations not to look at your data. You are, for free, letting profit-driven entities, to store your data. Of course they could claim to be implementing end-to-end encryption, or that the free tier is a trail of their paid subscription services. As far as I know none of this is open source either, so you're just trusting big-data to hold your data for free without looking at your data. I just don't trust them not to look at my assignments I guess.
|
||||||
|
|
||||||
I'm not a fan of subscription services. If I can at moderate difficulty not depend on a corporation to keep their subscription prices sane, I will. I'm not sure how much I save by selfhosting nextcloud, but I also have the piece of mind that that price will never change (excluding buying additional drives or electricity price fluctuations).
|
I'm not a fan of subscription services. If I can at moderate difficulty not depend on a corporation to keep their subscription prices sane, I will. I'm not sure how much I save by selfhosting nextcloud, but I also have the piece of mind that that price will never change (excluding buying additional drives or electricity price fluctuations).
|
||||||
|
|
||||||
|
|
||||||
=== How I selfhost Nextcloud
|
== How I selfhost Nextcloud
|
||||||
|
|
||||||
===== Network and server configurations
|
=== Network and server configurations
|
||||||
I already had an old laptop kicking around, a domain name, and reverse proxy on a static IP. A domain name isn't even really nessecary, unless you want a polished experience.
|
I already had an old laptop kicking around, a domain name, and reverse proxy on a static IP. A domain name isn't even really necessary, unless you want a polished experience.
|
||||||
|
|
||||||
My laptop already runs linux with docker installed, which would only take a few hours to set up otherwise.
|
My laptop already runs linux with docker installed, which would only take a few hours to set up otherwise.
|
||||||
From there I used docker compose to start the #link("https://github.com/nextcloud/all-in-one")[nextcloud all in one].
|
From there I used docker compose to start the #link("https://github.com/nextcloud/all-in-one")[nextcloud all in one].
|
||||||
@@ -29,7 +29,7 @@ From there I used docker compose to start the #link("https://github.com/nextclou
|
|||||||
On my reverse proxy I pointed the `nextcloud` subnet at my home IP in its Caddy file. Caddy is a lot like NGINX except its easier to set up for my use case. My IP doesn't change enough to warrent setting up DDNS, however thats always an option. After the ports are opened the hard part is opening the firewall. The reason this is hard is because I always forget the firewall and spend a solid chunk of time debugging why nothing works. In the end, if nothing is working, tools like `traceroute`, `dig` and `ping` are varying degrees of useful for testing accessibility.
|
On my reverse proxy I pointed the `nextcloud` subnet at my home IP in its Caddy file. Caddy is a lot like NGINX except its easier to set up for my use case. My IP doesn't change enough to warrent setting up DDNS, however thats always an option. After the ports are opened the hard part is opening the firewall. The reason this is hard is because I always forget the firewall and spend a solid chunk of time debugging why nothing works. In the end, if nothing is working, tools like `traceroute`, `dig` and `ping` are varying degrees of useful for testing accessibility.
|
||||||
|
|
||||||
|
|
||||||
===== Can I get five nines?
|
=== Can I get five nines?
|
||||||
No. 99.999% uptime won't happen. Power and network outages happen. Roommates unplug your server or brick the router or close your network ports. The thing is that when these things happen, I still have my files on any device. They just stop syncing temporarily, to resume and correct conflicts when the network is back up.
|
No. 99.999% uptime won't happen. Power and network outages happen. Roommates unplug your server or brick the router or close your network ports. The thing is that when these things happen, I still have my files on any device. They just stop syncing temporarily, to resume and correct conflicts when the network is back up.
|
||||||
|
|
||||||
With recent cloud outages in mind, I want it to be my own fault if I can't access my files. If tech giants side against Canada in some idiocratic war, I don't want to lose data. I don't care enough to implement RAID (yet), but if the server dies or the drive does get corrupted, I can reach into my server drawr, take out the drive, and load up ddrescue on my desktop. Also, the files are already syncing to atleast 3 other devices, data loss doesn't scare me. (Knocks on wood)
|
With recent cloud outages in mind, I want it to be my own fault if I can't access my files. If tech giants side against Canada in some idiocratic war, I don't want to lose data. I don't care enough to implement RAID (yet), but if the server dies or the drive does get corrupted, I can reach into my server drawr, take out the drive, and load up ddrescue on my desktop. Also, the files are already syncing to atleast 3 other devices, data loss doesn't scare me. (Knocks on wood)
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
#let post_slug = "assembly-game"
|
#let post_slug = "assembly-game"
|
||||||
#let post_preview_image = "phobos.png"
|
#let post_preview_image = "phobos.png"
|
||||||
#let post_summary = "Making a rocket platformer in assembly"
|
#let post_summary = "Coding a game in raw assembly"
|
||||||
|
|
||||||
= Phobos
|
= Phobos
|
||||||
|
|
||||||
|
|||||||
@@ -111,14 +111,21 @@ a {
|
|||||||
.content {
|
.content {
|
||||||
width: 90%;
|
width: 90%;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
|
margin-top: 3.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.content p {
|
.post a {
|
||||||
|
text-decoration: underline;
|
||||||
|
color:orangered;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post * {
|
||||||
text-align: left;
|
text-align: left;
|
||||||
}
|
}
|
||||||
|
|
||||||
.content li {
|
.top-image {
|
||||||
text-align: left;
|
max-height: 40vh;
|
||||||
|
max-width: 1000px;
|
||||||
}
|
}
|
||||||
|
|
||||||
nav ul {
|
nav ul {
|
||||||
@@ -141,8 +148,8 @@ nav ul {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#name {
|
#name {
|
||||||
font-size: 5rem;
|
font-size: clamp(3.5rem, 12vw , 5rem);
|
||||||
margin: 20px;
|
margin: 0px;
|
||||||
padding: 0px;
|
padding: 0px;
|
||||||
animation: nameShadow 5s linear infinite;
|
animation: nameShadow 5s linear infinite;
|
||||||
letter-spacing: 2px;
|
letter-spacing: 2px;
|
||||||
@@ -152,6 +159,7 @@ nav ul {
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
body {
|
body {
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
background-image: linear-gradient(330deg, #222, #040404);
|
background-image: linear-gradient(330deg, #222, #040404);
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
</ul>
|
</ul>
|
||||||
</nav>
|
</nav>
|
||||||
</header>
|
</header>
|
||||||
<div style="padding-top: 3rem" class="content">
|
<div class="content">
|
||||||
{% block body %}{% endblock %}
|
{% block body %}{% endblock %}
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
@@ -4,7 +4,8 @@
|
|||||||
|
|
||||||
<p id="name">Jeremy Janella</p>
|
<p id="name">Jeremy Janella</p>
|
||||||
|
|
||||||
|
<div class="post">
|
||||||
{{ content|safe }}
|
{{ content|safe }}
|
||||||
|
</div>
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@@ -2,6 +2,9 @@
|
|||||||
|
|
||||||
{% block body %}
|
{% block body %}
|
||||||
|
|
||||||
|
<div class="post">
|
||||||
|
<img src="{{ image }}" class="top-image">
|
||||||
{{ content|safe }}
|
{{ content|safe }}
|
||||||
|
</div>
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
Reference in New Issue
Block a user