feat: Adds pocket id authentication to caddy, adds server management article.
All checks were successful
CI / release (push) Successful in 6m31s

This commit is contained in:
2025-04-11 08:26:51 -04:00
parent f43a191908
commit b986fe41c3
14 changed files with 295 additions and 144 deletions

2
.gitignore vendored
View File

@@ -9,7 +9,7 @@ public/*
.hugo_build.lock
deploy
node_modules
env
.env
Package.resolved
# Local Netlify folder

53
Caddyfile Normal file
View File

@@ -0,0 +1,53 @@
{
# Port to listen on
http_port 80
# Configure caddy-security.
order authenticate before respond
security {
oauth identity provider generic {
delay_start 3
realm generic
driver generic
client_id {env.OAUTH_CLIENT_ID}
client_secret {env.OAUTH_CLIENT_SECRET}
scopes openid email profile
base_auth_url https://id.housh.dev
metadata_url https://id.housh.dev/.well-known/openid-configuration
}
authentication portal myportal {
crypto default token lifetime 3600 # Seconds until you have to re-authenticate
enable identity provider generic
cookie insecure on # Set to "on" if you're not using HTTPS
transform user {
match realm generic
action add role user
}
}
authorization policy mypolicy {
set auth url /caddy-security/oauth2/generic
allow roles user
inject headers with claims
}
}
}
https://docs.housh.dev {
@auth {
path /caddy-security/*
}
route @auth {
authenticate with myportal
}
route /* {
authorize with mypolicy
root * /app
file_server
}
}

View File

@@ -38,14 +38,17 @@ RUN npx -y pagefind --site deploy
# ==================================================
# Run Image
# ==================================================
FROM caddy:2.9.1-alpine
FROM ghcr.io/authcrunch/authcrunch:latest
WORKDIR /app
COPY --from=css /build/deploy .
COPY --from=css /build/content/static/output.css ./static/output.css
COPY --from=css /build/deploy/pagefind ./pagefind
COPY Caddyfile /etc/caddy/Caddyfile
RUN /usr/bin/caddy fmt --overwrite /etc/caddy/Caddyfile
EXPOSE 80
CMD ["/usr/bin/caddy", "file-server", "--root", "/app", "--listen", ":80"]
CMD ["/usr/bin/caddy", "run", "--config", "/etc/caddy/Caddyfile"]

View File

@@ -21,10 +21,10 @@ func baseLayout(
.documentType("html"),
html(lang: "en-US") {
generateHead(pageTitle, extraHeader)
body(class: "text-white text-lg pb-5 font-avenir \(section.rawValue)") {
body(class: "text-white text-lg font-avenir \(section.rawValue)") {
siteHeader(section)
div(class: "container mb-auto") {
div(class: "mb-auto") {
children()
}
if section == .articles {
@@ -57,9 +57,7 @@ private func siteHeader(_ section: Section) -> Node {
}
}
}
// if section == .home {
div(class: "font-avenir w-full pt-4", id: "search") {}
// }
}
}
}

View File

@@ -97,7 +97,9 @@ func renderArticle(context: ItemRenderingContext<ArticleMetadata>) -> Node {
title: context.item.title,
extraHeader: generateHeader(.article(context.item))
) {
article(class: "pt-8") {
article(class: "pt-8 mx-10") {
div(class: "bg-slate-800 py-10") {
div(class: "mx-10") {
h1 { context.item.title }
div {
renderArticleInfo(context.item)
@@ -107,15 +109,26 @@ func renderArticle(context: ItemRenderingContext<ArticleMetadata>) -> Node {
Node.raw(context.item.body)
}
}
}
}
div(class: "border-t border-light pt-8 mt-16", id: "recents") {
div(class: "border-t border-light p-10 mt-16", id: "recents") {
div(class: "grid lg:grid-cols-2") {
h4(class: "text-3xl text-amber-500 font-extrabold mb-8") { otherArticles.title }
if let tag = otherArticles.tag {
a(href: "/articles/tag/\(tag)") {
div(class: " [&:hover]:border-b border-orange px-5 flex flex-row gap-5") {
img(src: "/static/img/tag.svg", width: "40")
span(class: "text-4xl font-extrabold text-orange") { tag }
div(class: " [&:hover]:border-b border-green-500 px-5 flex flex-row gap-5") {
img(class: "-mt-2", src: "/static/img/tag.svg", width: "40")
div(class: "block") {
div(class: "block") {
span(class: "mt-2 text-4xl font-extrabold text-orange") { tag }
}
div(class: "block") {
span(class: "text-sm text-orange-400") {
"View related articles with this tag."
}
}
}
}
}
}
@@ -137,10 +150,11 @@ func renderArticle(context: ItemRenderingContext<ArticleMetadata>) -> Node {
}
}
func renderArticleForGrid(article: Item<ArticleMetadata>) -> Node {
section {
func renderArticleForGrid(article: Item<ArticleMetadata>, border: Bool = true) -> Node {
div(class: "bg-slate-800\(border ? " border border-slate-400 rounded-lg" : "")") {
section(class: "m-4") {
h3(class: "post-title text-2xl font-bold mb-2") {
a(class: "[&:hover]:border-b border-orange-400", href: article.url) { article.title }
a(class: "[&:hover]:border-b border-green-500", href: article.url) { article.title }
}
renderArticleInfo(article)
p {
@@ -151,4 +165,5 @@ func renderArticleForGrid(article: Item<ArticleMetadata>) -> Node {
}
}
}
}
}

View File

@@ -18,14 +18,16 @@ func renderArticles(context: ItemsRenderingContext<ArticleMetadata>) -> Node {
return baseLayout(canocicalURL: "/articles/", section: .articles, title: "Articles", rssLink: "", extraHeader: "") {
// TODO: Add list of tags here that can be navigated to.
sortedByYearDescending.map { year, articles in
div {
div(class: "mt-8 bg-slate-800") {
div(class: "pt-8 mx-10") {
div(class: "border-b border-light flex flex-row gap-4 mb-12") {
img(src: "/static/img/calendar.svg", width: "40")
h1(class: "text-4xl font-extrabold pt-3") { year }
}
div(class: "grid gap-10 mb-16") {
articles.map { renderArticleForGrid(article: $0) }
articles.map { renderArticleForGrid(article: $0, border: false) }
}
}
}
}
@@ -63,60 +65,6 @@ func renderYear<T>(context: PartitionedRenderingContext<T, ArticleMetadata>) ->
baseRenderArticles(context.items, canocicalURL: "/articles/\(context.key)/", title: "Articles in \(context.key)")
}
private struct SearchData: Encodable {
let url: String
let title: String
let body: String
init(article: Item<ArticleMetadata>) throws {
self.url = article.url
self.title = article.title
let rawContent: String = try article.absoluteSource.read()
self.body = Self.parse(rawContent)
}
/// Grabs the metadata (wrapped within `---`), the first title, and the body of the document.
static func parts(from content: String) -> (String?, String?, String) {
let scanner = Scanner(string: content)
var header: String? = nil
var title: String? = nil
if scanner.scanString("---") == "---" {
header = scanner.scanUpToString("---")
_ = scanner.scanString("---")
}
if scanner.scanString("# ") == "# " {
title = scanner.scanUpToString("\n")
}
let body = String(scanner.string[scanner.currentIndex...])
return (header, title, body)
}
static func parse(_ content: String) -> String {
let (_, _, body) = parts(from: content)
return body
.replacingOccurrences(of: "\n", with: " ")
.replacingOccurrences(of: "#", with: "")
}
}
func renderJson(_ articles: ItemsRenderingContext<ArticleMetadata>) throws -> String {
print(articles.items.count)
print(articles.items)
let data = try jsonEncoder.encode(articles.items.map(SearchData.init(article:)))
return String(data: data, encoding: .utf8)!
}
private let jsonEncoder: JSONEncoder = {
let encoder = JSONEncoder()
encoder.outputFormatting = [.prettyPrinted, .sortedKeys]
return encoder
}()
private func baseRenderArticles(
_ articles: [Item<ArticleMetadata>],
canocicalURL: String,

View File

@@ -31,6 +31,7 @@ func renderHome(body: String) -> Node {
Node.raw(body)
}
div {
div(class: "bg-slate-800 p-10 rounded-lg border border-slate-400") {
h2 { "Quick Links" }
div(class: "grid lg:grid-cols-2 gap-6") {
HomeLink.internal(
@@ -90,6 +91,7 @@ func renderHome(body: String) -> Node {
)
}
}
}
script(src: "https://unpkg.com/lucide@latest")
Node.raw("""
<script>

View File

@@ -3,6 +3,7 @@ services:
image: git.housh.dev/homelab/docs:latest
container_name: docs
restart: unless-stopped
env_file: .env
ports:
- ${PORT:-8081}:80
networks:

View File

@@ -0,0 +1,119 @@
---
date: 2025-04-09
tags: infrastructure, servers, homelab
primaryTag: infrastructure
---
# Server Management Console
This article I'll describe some steps to manage and / or trouble shoot the
servers.
## Management Console
The servers have a management console that is accessible from the internal
network. You will need to get the login name and password from Michael.
| Server | Link |
| ------------ | ---------------------------------------------------------------------- |
| mighty-mini | [console.mightymini.housh.dev](https://console.mightymini.housh.dev) |
| franken-mini | [console.frankenmini.housh.dev](https://console.frankenmini.housh.dev) |
| rogue-mini | [console.roguemini.housh.dev](https://console.roguemini.housh.dev) |
The management console allows you to update the server, check logs, and access a
terminal on the machine. If you are updating the server via the management
console, it is often required to reboot the server. All of the services are
setup to restart upon a reboot of the server, so that should not cause problems,
but you will be disconnected from the management console when the server shuts
down. It does take a few minutes generally for the servers to go through the
full boot process.
> Note: If something is not running the easiest thing to do would be to just
> reboot the servers and the services should restart.
[You can view the server and services status here.](https://uptime.housh.dev/status/housh-dev)
## Reboot the server
You can reboot the server from the management console in the `Overview` section
or by typing the following command in the terminal.
```bash
sudo reboot --now
```
## Useful Tips
There are several commands that may help trouble shoot the services on the
server. For these you will need to make sure to turn on administrative access by
clicking the button, if needed.
![console](/static/img/servermanagement.console.png)
All of the following commands can be entered into the `Terminal` section of the
console.
### Check the services are running
```bash
sudo docker ps --all
```
If working on a small screen or the output is bunched up then you can use the
following command to only reveal a smaller portion of the output.
```bash
sudo docker ps --format 'table {{.Names}}\t{{.Status}}'
```
![output](/static/img/servermanagement.dockerps.png)
Here you would look for services where the **_STATUS_** says `Exited` or if any
of the services say `unhealthy`.
### Service locations
The services are primary located in `/etc/komodo/stacks` or `~/containers`
directories. You can list the contents of those directories using the following
command.
```bash
ls -lah ~/containers
```
```bash
ls -lah /etc/komodo/stacks
```
### Starting services from the terminal
If you would like to ensure a service is up and running from the terminal move
into the directory of the service.
```bash
cd ~/containers/purchase-orders
```
And issue the following command
```bash
sudo docker compose up -d
```
### Check the logs of a running container
You can check the logs of a container in several different ways. The easiest is
if you know the containers name.
```bash
sudo docker logs -f purchase_orders
```
Or if you know the directory you can move into the directory using the `cd`
command and use the following.
```bash
sudo docker compose logs -f
```
To stop viewing the logs hit `Ctrl-c`.

Binary file not shown.

Binary file not shown.

View File

@@ -170,15 +170,15 @@ td {
}
table {
@apply mb-8;
@apply py-8 mb-6;
}
table td {
@apply px-6;
@apply px-6 py-2;
}
.container {
@apply px-10;
@apply py-20;
}
.container img {
@@ -193,3 +193,7 @@ blockquote {
blockquote p {
@apply px-6 pt-6 text-blue-600 font-semibold;
}
pre {
@apply mb-6;
}

File diff suppressed because one or more lines are too long

View File

@@ -1 +1,3 @@
PORT=8081
OAUTH_CLIENT_ID="<id>"
OAUTH_CLIENT_SECRET="<secret>"