10 Commits
Author SHA1 Message Date
nyeki 245adccf23 Release version v1.1.3 2026-01-02 00:44:11 -03:00
nyeki 19418db4a4 Release version v1.1.2 2026-01-02 00:38:37 -03:00
nyeki 462a21604b Add reverse proxy endpoint 2026-01-02 00:29:56 -03:00
nyeki 5bb1f42bb3 Add documentation to release.py 2025-07-13 21:52:27 +00:00
nyeki 4ab3efff06 Make script in release.py a constant 2025-07-13 21:46:52 +00:00
nyeki 3373363f43 Update commit messages on release 2025-07-13 19:21:58 +00:00
nyeki 123cdc65e3 Release 1.1.1 version 2025-07-13 16:17:50 -03:00
nyeki 164055dd2d Add healthcheck endpoint to router 2025-07-13 19:13:12 +00:00
nyeki 8dbdc23e52 Release 1.1.0 version 2025-07-13 16:11:25 -03:00
nyeki 446dc4b47f Add healthcheck endpoint 2025-07-13 19:07:12 +00:00
9 changed files with 254 additions and 40 deletions
+1
View File
@@ -0,0 +1 @@
3.13
Generated
+105 -19
View File
@@ -84,16 +84,19 @@ dependencies = [
[[package]]
name = "api"
version = "1.0.0"
version = "1.1.3"
dependencies = [
"axum",
"bitcode",
"clap",
"dedent",
"dotenvy",
"futures-util",
"hyper",
"reqwest",
"serde",
"tokio",
"tokio-util",
"turbostore",
"urlencoding",
]
@@ -123,7 +126,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "021e862c184ae977658b36c4500f7feac3221ca5da43e3f25bd04ab6c79a29b5"
dependencies = [
"axum-core",
"bytes",
"bytes 1.10.1",
"form_urlencoded",
"futures-util",
"http",
@@ -156,7 +159,7 @@ version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68464cd0412f486726fb3373129ef5d2993f90c34bc2bc1c1e9943b2f4fc7ca6"
dependencies = [
"bytes",
"bytes 1.10.1",
"futures-core",
"http",
"http-body",
@@ -233,6 +236,22 @@ version = "1.23.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c76a5792e44e4abe34d3abf15636779261d45a7450612059293d1d2cfc63422"
[[package]]
name = "byteorder"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
[[package]]
name = "bytes"
version = "0.4.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "206fdffcfa2df7cbe15601ef46c813fce0965eb3286db6b56c583b814b51c81c"
dependencies = [
"byteorder",
"iovec",
]
[[package]]
name = "bytes"
version = "1.10.1"
@@ -419,6 +438,12 @@ dependencies = [
"percent-encoding",
]
[[package]]
name = "futures"
version = "0.1.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3a471a38ef8ed83cd6e40aa59c1ffe17db6855c18e3604d9c4ed8c08ebc28678"
[[package]]
name = "futures-channel"
version = "0.3.31"
@@ -434,6 +459,23 @@ version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e"
[[package]]
name = "futures-io"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6"
[[package]]
name = "futures-macro"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "futures-sink"
version = "0.3.31"
@@ -453,9 +495,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81"
dependencies = [
"futures-core",
"futures-io",
"futures-macro",
"futures-sink",
"futures-task",
"memchr",
"pin-project-lite",
"pin-utils",
"slab",
"tokio-io",
]
[[package]]
@@ -500,7 +548,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "17da50a276f1e01e0ba6c029e47b7100754904ee8a278f886546e98575380785"
dependencies = [
"atomic-waker",
"bytes",
"bytes 1.10.1",
"fnv",
"futures-core",
"futures-sink",
@@ -530,7 +578,7 @@ version = "1.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565"
dependencies = [
"bytes",
"bytes 1.10.1",
"fnv",
"itoa",
]
@@ -541,7 +589,7 @@ version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184"
dependencies = [
"bytes",
"bytes 1.10.1",
"http",
]
@@ -551,7 +599,7 @@ version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a"
dependencies = [
"bytes",
"bytes 1.10.1",
"futures-core",
"http",
"http-body",
@@ -572,13 +620,14 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
[[package]]
name = "hyper"
version = "1.6.0"
version = "1.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cc2b571658e38e0c01b1fdca3bbbe93c00d3d71693ff2770043f8c29bc7d6f80"
checksum = "2ab2d4f250c3d7b1c9fcdff1cece94ea4e2dfbec68614f7b87cb205f24ca9d11"
dependencies = [
"bytes",
"atomic-waker",
"bytes 1.10.1",
"futures-channel",
"futures-util",
"futures-core",
"h2",
"http",
"http-body",
@@ -586,6 +635,7 @@ dependencies = [
"httpdate",
"itoa",
"pin-project-lite",
"pin-utils",
"smallvec",
"tokio",
"want",
@@ -613,7 +663,7 @@ version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0"
dependencies = [
"bytes",
"bytes 1.10.1",
"http-body-util",
"hyper",
"hyper-util",
@@ -630,7 +680,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f66d5bd4c6f02bf0542fad85d626775bab9258cf795a4256dcaf3161114d1df"
dependencies = [
"base64",
"bytes",
"bytes 1.10.1",
"futures-channel",
"futures-core",
"futures-util",
@@ -801,6 +851,15 @@ dependencies = [
"libc",
]
[[package]]
name = "iovec"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e"
dependencies = [
"libc",
]
[[package]]
name = "ipnet"
version = "2.11.0"
@@ -1056,9 +1115,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cbc931937e6ca3a06e3b6c0aa7841849b160a90351d6ab467a8b9b9959767531"
dependencies = [
"base64",
"bytes",
"bytes 1.10.1",
"encoding_rs",
"futures-core",
"futures-util",
"h2",
"http",
"http-body",
@@ -1080,12 +1140,14 @@ dependencies = [
"sync_wrapper",
"tokio",
"tokio-native-tls",
"tokio-util",
"tower",
"tower-http",
"tower-service",
"url",
"wasm-bindgen",
"wasm-bindgen-futures",
"wasm-streams",
"web-sys",
]
@@ -1396,7 +1458,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0cc3a2344dafbe23a245241fe8b09735b521110d30fcefbbd5feb1797ca35d17"
dependencies = [
"backtrace",
"bytes",
"bytes 1.10.1",
"io-uring",
"libc",
"mio",
@@ -1407,6 +1469,17 @@ dependencies = [
"windows-sys 0.52.0",
]
[[package]]
name = "tokio-io"
version = "0.1.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57fc868aae093479e3131e3d165c93b1c7474109d13c90ec0dda2a1bbfff0674"
dependencies = [
"bytes 0.4.12",
"futures",
"log",
]
[[package]]
name = "tokio-macros"
version = "2.5.0"
@@ -1440,11 +1513,11 @@ dependencies = [
[[package]]
name = "tokio-util"
version = "0.7.15"
version = "0.7.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "66a539a9ad6d5d281510d5bd368c973d636c02dbf8a67300bfb6b950696ad7df"
checksum = "2efa149fe76073d6e8fd97ef4f4eca7b67f599660115591483572e406e165594"
dependencies = [
"bytes",
"bytes 1.10.1",
"futures-core",
"futures-sink",
"pin-project-lite",
@@ -1474,7 +1547,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2"
dependencies = [
"bitflags",
"bytes",
"bytes 1.10.1",
"futures-util",
"http",
"http-body",
@@ -1676,6 +1749,19 @@ dependencies = [
"unicode-ident",
]
[[package]]
name = "wasm-streams"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "15053d8d85c7eccdbefef60f06769760a563c7f0a9d6902a13d35c7800b0ad65"
dependencies = [
"futures-util",
"js-sys",
"wasm-bindgen",
"wasm-bindgen-futures",
"web-sys",
]
[[package]]
name = "web-sys"
version = "0.3.77"
+5 -2
View File
@@ -1,6 +1,6 @@
[package]
name = "api"
version = "1.0.0"
version = "1.1.3"
edition = "2024"
[dependencies]
@@ -9,8 +9,11 @@ bitcode = "0.6.6"
clap = { version = "4.5.41", features = ["derive", "env"] }
dedent = "0.1.1"
dotenvy = "0.15.7"
reqwest = { version = "0.12.22", features = ["json"] }
futures-util = { version = "0.3.31", features = ["tokio-io"] }
hyper = "1.8.1"
reqwest = { version = "0.12.22", features = ["json", "stream"] }
serde = { version = "1.0.219", features = ["derive"] }
tokio = { version = "1.46.1", features = ["macros", "rt-multi-thread"] }
tokio-util = { version = "0.7.17", features = ["io"] }
turbostore = "0.2.0"
urlencoding = "2.1.3"
+6
View File
@@ -0,0 +1,6 @@
def main():
print("Hello from api!")
if __name__ == "__main__":
main()
+12
View File
@@ -0,0 +1,12 @@
[project]
name = "api"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.13"
dependencies = []
[dependency-groups]
dev = [
"toml>=0.10.2",
]
+28 -16
View File
@@ -1,35 +1,47 @@
"""
A release script to build and tag docker files, commit changes, and create release tags.
It requires `toml` to be installed (in Ubuntu via `sudo apt install python3 python3-toml` and then
`python3 release.py`).
Bump the version manually in Cargo.toml before running this script. You'll be asked for
confirmation before running all the steps, so you're safe if you forget.
"""
import os
import toml
def get_release_version():
"""
Reads the version from the Cargo.toml file.
"""
cargo_toml_path = os.path.join(os.path.dirname(__file__), 'Cargo.toml')
with open(cargo_toml_path, 'r') as f:
cargo_toml_path = os.path.join(os.path.dirname(__file__), "Cargo.toml")
with open(cargo_toml_path, "r", encoding="UTF-8") as f:
cargo_toml = toml.load(f)
return cargo_toml['package']['version']
return cargo_toml["package"]["version"]
version = get_release_version()
input(f"The current version is v{version}. Is this properly bumped? (Enter/Ctrl+C)")
script = f"""
docker build -t registry.nyeki.dev/nyeki/api:latest .
docker push registry.nyeki.dev/nyeki/api:latest
docker tag registry.nyeki.dev/nyeki/api:latest registry.nyeki.dev/nyeki/api:v{version}
docker push registry.nyeki.dev/nyeki/api:v{version}
SCRIPT = f"""
docker build -t registry.nyeki.dev/nyeki/api:latest .
docker push registry.nyeki.dev/nyeki/api:latest
docker tag registry.nyeki.dev/nyeki/api:latest registry.nyeki.dev/nyeki/api:v{version}
docker push registry.nyeki.dev/nyeki/api:v{version}
git add .
git commit -m "Release {version} version"
git tag -a v{version} -m "Release version {version}"
git push origin main
git push origin v{version}
git add .
git commit -m "Release version v{version}"
git tag -a v{version} -m "Release version v{version}"
git push origin main
git push origin v{version}
"""
os.system(script)
os.system(SCRIPT)
print(f"v{version} was released. Congratulations!")
+20 -2
View File
@@ -10,6 +10,7 @@ pub mod v1;
pub fn router() -> Router<Arc<State>> {
Router::new()
.route("/", get(docs))
.route("/healthcheck", get(healthcheck))
.nest("/v1", v1::router())
}
@@ -17,8 +18,9 @@ async fn docs() -> &'static str {
dedent!(
"
Welcome to Nyeki's API! This is a tiny API I built to provide some basic information I
found myself needing in more than one place. This at the moment only includes Discord and
GitHub profiles, but I may add more stuff as I see myself needing it.
found myself needing in more than one place. This at the moment only includes Discord
profiles, GitHub profiles, and a reverse proxy, but I may add more stuff as I see myself
needing it.
The API is versioned, so API endpoints will always be prefixed with `/v{version}`. The
current version is 1 (i.e. stuff is prefixed by `/v1`). All responses are JSON (or
@@ -31,6 +33,9 @@ async fn docs() -> &'static str {
The current endpoints are:
GET /healthcheck
Returns OK when the API is running.
GET /v1/profile
Return's the user's profile (username, name, avatar, banner, and URL).
@@ -84,6 +89,15 @@ async fn docs() -> &'static str {
for supported sources).
- `source`: The source of the profile (either `discord` or `github`).
GET /v1/proxy
Proxies a request to the given URL.
Note that headers are not forwarded and neither is the request body. This is a
simple GET proxy.
Query parameters:
- `url`: The URL to proxy the request to.
They all take 2 query parameters: `id` and `source`. The `id` is the ID of the user you
want to get the profile of, and the `source` is the source of the profile. The source can
be either `discord` or `github`.
@@ -113,3 +127,7 @@ async fn docs() -> &'static str {
"
)
}
async fn healthcheck() -> &'static str {
"OK"
}
+50 -1
View File
@@ -4,10 +4,12 @@ use std::sync::Arc;
use axum::{
Json, Router,
body::{Body, Bytes},
extract::{Query, State as AxumState},
response::Redirect,
response::{Redirect, Response},
routing::get,
};
use futures_util::StreamExt;
use reqwest::Client;
use serde::{Deserialize, Serialize};
use turbostore::{Decode, Duration, Encode};
@@ -21,6 +23,7 @@ pub fn router() -> Router<Arc<State>> {
.route("/profile/avatar", get(get_profile_avatar))
.route("/profile/banner", get(get_profile_banner))
.route("/profile/url", get(get_profile_url))
.route("/proxy", get(proxy_with_cors))
.fallback(error_404)
.method_not_allowed_fallback(error_405)
}
@@ -300,3 +303,49 @@ async fn get_profile_url(
Ok(Redirect::to(&profile.url))
}
#[derive(Debug, Deserialize)]
struct ProxyQuery {
url: String,
}
async fn proxy_with_cors(Query(params): Query<ProxyQuery>) -> Result<Response, Error> {
let client = Client::new();
match client.get(&params.url).send().await {
Ok(resp) => {
let status = resp.status();
let headers = resp.headers().clone();
// Turn the reqwest response into a streaming body
let stream = resp.bytes_stream().map(|chunk_result| match chunk_result {
Ok(chunk) => Ok::<Bytes, std::io::Error>(chunk),
Err(_) => Err(std::io::Error::other("stream error")),
});
// Convert the stream into a hyper Body
let body = Body::from_stream(stream);
// Build response
let mut response = Response::builder().status(status);
let headers_mut = response.headers_mut().unwrap();
for (key, value) in headers.iter() {
// Skip certain headers that hyper will manage itself
if key.as_str().eq_ignore_ascii_case("content-length") {
continue;
}
headers_mut.insert(key, value.clone());
}
// Insert CORS headers
headers_mut.insert("access-control-allow-origin", "*".parse().unwrap());
headers_mut.insert("access-control-allow-methods", "GET, OPTIONS".parse().unwrap());
headers_mut.insert("access-control-allow-headers", "*".parse().unwrap());
Ok(response.body(body).unwrap())
}
Err(_) => Err(Error {
status: 500,
message: "Failed to fetch the requested URL".into(),
}),
}
}
Generated
+27
View File
@@ -0,0 +1,27 @@
version = 1
revision = 3
requires-python = ">=3.13"
[[package]]
name = "api"
version = "0.1.0"
source = { virtual = "." }
[package.dev-dependencies]
dev = [
{ name = "toml" },
]
[package.metadata]
[package.metadata.requires-dev]
dev = [{ name = "toml", specifier = ">=0.10.2" }]
[[package]]
name = "toml"
version = "0.10.2"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/be/ba/1f744cdc819428fc6b5084ec34d9b30660f6f9daaf70eead706e3203ec3c/toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f", size = 22253, upload-time = "2020-11-01T01:40:22.204Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/44/6f/7120676b6d73228c96e17f1f794d8ab046fc910d781c8d151120c3f1569e/toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b", size = 16588, upload-time = "2020-11-01T01:40:20.672Z" },
]