-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 35ddc90
Showing
19 changed files
with
1,273 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
name: 💜 Test | ||
|
||
on: | ||
push: | ||
branches: | ||
- main | ||
pull_request: | ||
|
||
jobs: | ||
test: | ||
runs-on: ubuntu-latest | ||
steps: | ||
- uses: actions/checkout@v4 | ||
- uses: erlef/setup-beam@v1 | ||
with: | ||
otp-version: "26.0.2" | ||
gleam-version: "1.2.1" | ||
rebar3-version: "3" | ||
# elixir-version: "1.15.4" | ||
- run: gleam deps download | ||
- run: gleam test | ||
- run: gleam format --check src test |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
# Gleam | ||
*.beam | ||
*.ez | ||
/build | ||
erl_crash.dump | ||
|
||
# Visual Studio Code | ||
.vscode |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
VERSION 0.8 | ||
|
||
all: | ||
BUILD +docker | ||
|
||
docker: | ||
ARG tag=latest | ||
|
||
FROM ghcr.io/gleam-lang/gleam:v1.2.1-erlang-alpine | ||
|
||
COPY +build/erlang-shipment/ /momoka/erlang-shipment/ | ||
|
||
WORKDIR /momoka/ | ||
|
||
ENTRYPOINT ["./erlang-shipment/entrypoint.sh"] | ||
|
||
CMD ["run"] | ||
|
||
SAVE IMAGE --push fuwn/momoka:${tag} | ||
|
||
deps: | ||
FROM ghcr.io/gleam-lang/gleam:v1.2.0-erlang-alpine | ||
|
||
RUN apk add --no-cache build-base | ||
|
||
build: | ||
FROM +deps | ||
|
||
WORKDIR /momoka/ | ||
|
||
COPY src/ /momoka/src/ | ||
COPY gleam.toml /momoka/ | ||
COPY manifest.toml /momoka/ | ||
|
||
RUN gleam build \ | ||
&& cd build/ \ | ||
&& gleam export erlang-shipment | ||
|
||
SAVE ARTIFACT /momoka/build/erlang-shipment/ |
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
# 🏕️ Momoka | ||
|
||
> A Gemini-to-Gopher Proxy | ||
Momoka is a Gopher proxy that sits in between Gopher clients and Gemini servers. | ||
It translates any Gemini requests containing Gemtext into Gopher-compatible | ||
responses. | ||
|
||
Momoka is written in under 300 (280) lines of code and written in the functional | ||
[Gleam](https://gleam.run). It's designed to be small and simple. | ||
|
||
## Usage | ||
|
||
If you'd like to test out a production deployment of Momoka, you can visit | ||
[`gopher://fuwn.me:70/1`](gopher://fuwn.me:70/1). | ||
|
||
### Local | ||
|
||
```bash | ||
$ git clone git@github.com:Fuwn/momoka.git | ||
$ cd momoka | ||
$ gleam run | ||
$ # or | ||
$ nix run | ||
``` | ||
|
||
### Docker | ||
|
||
```shell | ||
docker run -p '70:70' --rm fuwn/momoka:latest | ||
``` | ||
|
||
### Proxy | ||
|
||
By default, top-level requests, like `gopher://fuwn.me/1`, are proxied to | ||
their mapped Gemini equivalents. Here are a few examples. | ||
|
||
- [`gopher://fuwn.me`](gopher://fuwn.me) => | ||
[`gemini://fuwn.me`](gemini://fuwn.me) | ||
- [`gopher://fuwn.me/1`](gopher://fuwn.me/1) => | ||
[`gemini://fuwn.me`](gemini://fuwn.me) | ||
- [`gopher://fuwn.me/1/index2`](gopher://fuwn.me/1/index2) => | ||
[`gemini://fuwn.me/index2`](gemini://fuwn.me/index2) | ||
|
||
Prepending `/proxy/` to the path will allow you to proxy any Gemini server. | ||
Here are a few examples. | ||
|
||
- [`gopher://fuwn.me/1/proxy/geminiprotocol.net`](gopher://fuwn.me/1/proxy/geminiprotocol.net) | ||
=> [`gemini://geminiprotocol.net`](gemini://geminiprotocol.net) | ||
- [`gopher://fuwn.me/1/proxy/fuwn.me/index2`](gopher://fuwn.me/1/proxy/fuwn.me/index2) | ||
=> [`gemini://fuwn.me/index2`](`gemini://fuwn.me/index2`) | ||
|
||
### Configuration | ||
|
||
Momoka contains three environment variables that can be set to your liking. | ||
|
||
- `ROOT` – The root Gemini capsule to proxy for top-level requests (defaults to | ||
`fuwn.me`) | ||
- `PORT` – The port to listen on for Gopher clients (defaults to `70`) | ||
- `GEMINI_PROXY` – A raw-Gemtext producing Gemini-to-HTTP proxy. (defaults to | ||
the [fuwn.me](https://fuwn.me) | ||
[September](https://github.com/gemrest/september) instance) | ||
|
||
## GemRest | ||
|
||
I'm also the author of [GemRest](https://github.com/gemrest), the largest | ||
organisation of Gemini-oriented software, tooling, and libraries. If you're | ||
interested in Gemini, I'd recommend checking it out. | ||
|
||
## Licence | ||
|
||
This project is licensed with the [GNU General Public License v3.0](./LICENSE). |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
{ | ||
description = "Gemini-to-Gopher Proxy"; | ||
inputs = { | ||
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; | ||
flake-utils.url = "github:numtide/flake-utils"; | ||
nix-gleam.url = "github:arnarg/nix-gleam"; | ||
gitignore = { | ||
url = "github:hercules-ci/gitignore.nix"; | ||
inputs.nixpkgs.follows = "nixpkgs"; | ||
}; | ||
}; | ||
outputs = { self, nixpkgs, flake-utils, nix-gleam, gitignore, ... }: | ||
flake-utils.lib.eachDefaultSystem (system: | ||
let | ||
pkgs = import nixpkgs { | ||
inherit system; | ||
overlays = [ | ||
nix-gleam.overlays.default | ||
]; | ||
}; | ||
inherit (gitignore.lib) gitignoreSource; | ||
in | ||
{ | ||
packages.default = pkgs.buildGleamApplication { | ||
src = gitignoreSource ./.; | ||
rebar3Package = pkgs.rebar3WithPlugins { | ||
plugins = with pkgs.beamPackages; [ pc ]; | ||
}; | ||
}; | ||
devShell = pkgs.mkShell { | ||
buildInputs = [ pkgs.gleam pkgs.rebar3 ]; | ||
}; | ||
} | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
# For a full reference of all the available options, you can have a look at | ||
# https://gleam.run/writing-gleam/gleam-toml/. | ||
|
||
name = "momoka" | ||
version = "1.0.0" | ||
gleam = ">= 0.2.1" | ||
description = "Gemini-to-Gopher Proxy" | ||
licenses = ["GPL-3.0-only"] | ||
repository = { type = "github", user = "Fuwn", repo = "momoka" } | ||
|
||
[dependencies] | ||
gleam_stdlib = ">= 0.34.0 and < 2.0.0" | ||
glisten = ">= 3.0.0 and < 4.0.0" | ||
gleam_erlang = ">= 0.25.0 and < 1.0.0" | ||
gleam_otp = ">= 0.10.0 and < 1.0.0" | ||
gleam_httpc = ">= 2.2.0 and < 3.0.0" | ||
gleam_http = ">= 3.6.0 and < 4.0.0" | ||
envoy = ">= 1.0.1 and < 2.0.0" | ||
|
||
[dev-dependencies] | ||
gleeunit = ">= 1.0.0 and < 2.0.0" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
# This file was generated by Gleam | ||
# You typically do not need to edit this file | ||
|
||
packages = [ | ||
{ name = "envoy", version = "1.0.1", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "envoy", source = "hex", outer_checksum = "CFAACCCFC47654F7E8B75E614746ED924C65BD08B1DE21101548AC314A8B6A41" }, | ||
{ name = "gleam_erlang", version = "0.25.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_erlang", source = "hex", outer_checksum = "054D571A7092D2A9727B3E5D183B7507DAB0DA41556EC9133606F09C15497373" }, | ||
{ name = "gleam_http", version = "3.6.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_http", source = "hex", outer_checksum = "8C07DF9DF8CC7F054C650839A51C30A7D3C26482AC241C899C1CEA86B22DBE51" }, | ||
{ name = "gleam_httpc", version = "2.2.0", build_tools = ["gleam"], requirements = ["gleam_http", "gleam_stdlib"], otp_app = "gleam_httpc", source = "hex", outer_checksum = "CF76C71002DEECF6DC5D9CA83D962728FAE166B57926BE442D827004D3C7DF1B" }, | ||
{ name = "gleam_otp", version = "0.10.0", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_stdlib"], otp_app = "gleam_otp", source = "hex", outer_checksum = "0B04FE915ACECE539B317F9652CAADBBC0F000184D586AAAF2D94C100945D72B" }, | ||
{ name = "gleam_stdlib", version = "0.38.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "663CF11861179AF415A625307447775C09404E752FF99A24E2057C835319F1BE" }, | ||
{ name = "gleeunit", version = "1.1.2", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleeunit", source = "hex", outer_checksum = "72CDC3D3F719478F26C4E2C5FED3E657AC81EC14A47D2D2DEBB8693CA3220C3B" }, | ||
{ name = "glisten", version = "3.0.0", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_otp", "gleam_stdlib", "logging", "telemetry"], otp_app = "glisten", source = "hex", outer_checksum = "AEB79A7244CB553A52974EDB87E652607E82749E4EC84B47B1F1BC926F1673C8" }, | ||
{ name = "logging", version = "1.1.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "logging", source = "hex", outer_checksum = "A996064F04EF6E67F0668FD0ACFB309830B05D0EE3A0C11BBBD2D4464334F792" }, | ||
{ name = "telemetry", version = "1.2.1", build_tools = ["rebar3"], requirements = [], otp_app = "telemetry", source = "hex", outer_checksum = "DAD9CE9D8EFFC621708F99EAC538EF1CBE05D6A874DD741DE2E689C47FEAFED5" }, | ||
] | ||
|
||
[requirements] | ||
envoy = { version = ">= 1.0.1 and < 2.0.0"} | ||
gleam_erlang = { version = ">= 0.25.0 and < 1.0.0" } | ||
gleam_http = { version = ">= 3.6.0 and < 4.0.0" } | ||
gleam_httpc = { version = ">= 2.2.0 and < 3.0.0" } | ||
gleam_otp = { version = ">= 0.10.0 and < 1.0.0" } | ||
gleam_stdlib = { version = ">= 0.34.0 and < 2.0.0" } | ||
gleeunit = { version = ">= 1.0.0 and < 2.0.0" } | ||
glisten = { version = ">= 3.0.0 and < 4.0.0" } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
import envoy | ||
import gemtext/parse | ||
import gleam/bit_array | ||
import gleam/bytes_builder | ||
import gleam/http/request | ||
import gleam/httpc | ||
import gleam/string | ||
import gopher | ||
|
||
pub fn get_gemtext_from_capsule(message) { | ||
let root_capsule = case envoy.get("ROOT") { | ||
Ok(capsule) -> capsule | ||
_ -> "fuwn.me" | ||
} | ||
// Gleam isn't mature enough to even have an SSL/TLS libraries that I could | ||
// find, and Erlang FFI is unclear, so Momoka uses the | ||
// [September](https://github.com/gemrest/september) proxy deployed at | ||
// <https://fuwn.me> to fetch the raw Gemini content. | ||
// | ||
// I'm sure as the language grows, this will be replaced with a more direct | ||
// approach. | ||
let gemini_proxy = case envoy.get("GEMINI_PROXY") { | ||
Ok(proxy) -> proxy | ||
_ -> "https://fuwn.me/raw/" | ||
} | ||
let assert Ok(request) = case bit_array.to_string(message) { | ||
Ok(path) -> { | ||
case path { | ||
"/\r\n" | "\r\n" -> request.to(gemini_proxy <> root_capsule) | ||
"/proxy/" <> route -> | ||
request.to(gemini_proxy <> string.replace(route, "\r\n", "")) | ||
"/" <> path -> | ||
request.to( | ||
gemini_proxy | ||
<> root_capsule | ||
<> "/" | ||
<> string.replace(path, "\r\n", ""), | ||
) | ||
_ -> request.to(root_capsule <> string.replace(path, "\r\n", "")) | ||
} | ||
} | ||
_ -> request.to(root_capsule) | ||
} | ||
let assert Ok(response) = httpc.send(request) | ||
|
||
bytes_builder.from_string( | ||
gopher.gemtext_to_gopher(parse.parse_gemtext_raw(response.body)) <> "\r\n", | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
import gemtext/gemtext.{type Gemtext} | ||
import gleam/list | ||
import gleam/string | ||
|
||
pub fn combine_adjacent_blockquote_lines(lines: List(Gemtext)) -> List(Gemtext) { | ||
case lines { | ||
[gemtext.BlockquoteLine(a), gemtext.BlockquoteLine(b), ..rest] -> | ||
combine_adjacent_blockquote_lines([ | ||
gemtext.Blockquote(string.join([a, b], "\n")), | ||
..rest | ||
]) | ||
[gemtext.Blockquote(a), gemtext.BlockquoteLine(b), ..rest] -> | ||
combine_adjacent_blockquote_lines([ | ||
gemtext.Blockquote(string.join([a, b], "\n")), | ||
..rest | ||
]) | ||
[g, ..rest] -> list.append([g], combine_adjacent_blockquote_lines(rest)) | ||
[] -> [] | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
import gleam/option.{type Option} | ||
|
||
pub type Gemtext { | ||
Text(String) | ||
Link(to: String, Option(String)) | ||
Heading(String, depth: Int) | ||
ListLine(String) | ||
List(List(List(String))) | ||
BlockquoteLine(String) | ||
Blockquote(String) | ||
Preformatted(description: Option(String), body: String) | ||
PreformattedLine(String) | ||
Whitespace | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import gleam/string | ||
|
||
pub fn count_leading_hashes(line: String) -> Int { | ||
do_count_leading_hashes(string.to_graphemes(line), 0) | ||
} | ||
|
||
fn do_count_leading_hashes(characters: List(String), accumulator: Int) -> Int { | ||
case characters { | ||
[c, ..rest] -> { | ||
case c { | ||
"#" -> do_count_leading_hashes(rest, accumulator + 1) | ||
_ -> accumulator | ||
} | ||
} | ||
_ -> accumulator | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
import gemtext/gemtext.{type Gemtext} | ||
import gleam/list | ||
|
||
pub fn group_adjacent_list_items(lines: List(Gemtext)) -> List(Gemtext) { | ||
case lines { | ||
[gemtext.ListLine(a), gemtext.ListLine(b), ..rest] -> | ||
group_adjacent_list_items([gemtext.List([[a], [b]]), ..rest]) | ||
[gemtext.List(lists), gemtext.ListLine(item), ..rest] -> | ||
group_adjacent_list_items([ | ||
gemtext.List(list.append(lists, [[item]])), | ||
..rest | ||
]) | ||
[g, ..rest] -> list.append([g], group_adjacent_list_items(rest)) | ||
[] -> [] | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
import gemtext/blockquote | ||
import gemtext/gemtext.{type Gemtext} | ||
import gemtext/heading | ||
import gemtext/list as gemtext_list | ||
import gemtext/preformatted_block | ||
import gleam/list | ||
import gleam/option.{None, Some} | ||
import gleam/string | ||
|
||
pub fn parse_gemtext_line(line) -> Gemtext { | ||
let trimmed_line = string.trim(line) | ||
|
||
case string.split(trimmed_line, " ") { | ||
["=>", to] -> gemtext.Link(to, None) | ||
["=>", to, ..title] -> gemtext.Link(to, Some(string.join(title, " "))) | ||
[">", ..rest] -> | ||
gemtext.BlockquoteLine(string.trim_left(string.join(rest, " "))) | ||
["*", ..rest] -> gemtext.ListLine(string.trim_left(string.join(rest, " "))) | ||
[""] -> gemtext.Whitespace | ||
_ -> { | ||
case string.to_graphemes(trimmed_line) { | ||
["#", ..rest] -> | ||
gemtext.Heading( | ||
string.trim_left(string.replace(string.join(rest, ""), "#", "")), | ||
heading.count_leading_hashes(line), | ||
) | ||
["`", "`", "`", ..rest] -> | ||
gemtext.PreformattedLine(string.trim_left( | ||
"```\n" <> string.join(rest, ""), | ||
)) | ||
_ -> gemtext.Text(line) | ||
} | ||
} | ||
} | ||
} | ||
|
||
pub fn parse_gemtext(text: String) -> List(Gemtext) { | ||
string.split(text, "\n") | ||
|> preformatted_block.group_adjacent_preformatted_block_lines | ||
|> list.map(parse_gemtext_line) | ||
|> gemtext_list.group_adjacent_list_items | ||
|> blockquote.combine_adjacent_blockquote_lines | ||
} | ||
|
||
pub fn parse_gemtext_raw(text: String) -> List(Gemtext) { | ||
string.split(text, "\n") | ||
|> preformatted_block.group_adjacent_preformatted_block_lines | ||
|> list.map(parse_gemtext_line) | ||
} |
Oops, something went wrong.